mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-28 16:45:32 +00:00
752 lines
26 KiB
Plaintext
752 lines
26 KiB
Plaintext
{{ $model := .model }}
|
|
{{ $images := $model.Images }}
|
|
|
|
<div class="space-y-8 h-full relative" id="newspaper-content">
|
|
<!-- Main Issue Pages -->
|
|
{{ if $images.MainPages }}
|
|
{{ $pages := $images.MainPages }}
|
|
{{ $pageCount := len $pages }}
|
|
|
|
<div class="space-y-6">
|
|
|
|
<!-- First page (single) -->
|
|
{{ if ge $pageCount 1 }}
|
|
{{ $firstPage := index $pages 0 }}
|
|
{{ if $firstPage.Available }}
|
|
<div class="newspaper-page-container" id="page-{{ $firstPage.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-image-line text-blue-600"></i>
|
|
<span class="page-indicator text-sm font-bold text-slate-600 bg-blue-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $firstPage.PageNumber }}">{{ $firstPage.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="single-page bg-white p-4 rounded-lg border border-slate-200 hover:border-slate-300 transition-colors duration-200">
|
|
<img src="{{ $firstPage.ImagePath }}"
|
|
alt="Seite {{ $firstPage.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $firstPage.PageNumber }}, false)"
|
|
data-page="{{ $firstPage.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
|
|
<!-- Middle pages (double spread) -->
|
|
{{ if ge $pageCount 4 }}
|
|
{{ $middlePage1 := index $pages 1 }}
|
|
{{ $middlePage2 := index $pages 2 }}
|
|
{{ if and $middlePage1.Available $middlePage2.Available }}
|
|
<div class="newspaper-page-container" id="page-{{ $middlePage1.PageNumber }}-{{ $middlePage2.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-copy-2-line text-blue-600"></i>
|
|
<span class="page-indicator text-sm font-bold text-slate-600 bg-blue-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $middlePage1.PageNumber }}">{{ $middlePage1.PageNumber }}-{{ $middlePage2.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="double-spread bg-white p-4 rounded-lg border border-slate-200 hover:border-slate-300 transition-colors duration-200">
|
|
<img src="{{ $middlePage1.ImagePath }}"
|
|
alt="Seite {{ $middlePage1.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $middlePage1.PageNumber }}, true)"
|
|
data-page="{{ $middlePage1.PageNumber }}"
|
|
loading="lazy">
|
|
<img src="{{ $middlePage2.ImagePath }}"
|
|
alt="Seite {{ $middlePage2.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $middlePage2.PageNumber }}, true)"
|
|
data-page="{{ $middlePage2.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
|
|
<!-- Last page (single) -->
|
|
{{ if ge $pageCount 4 }}
|
|
{{ $lastPage := index $pages 3 }}
|
|
{{ if $lastPage.Available }}
|
|
<div class="newspaper-page-container" id="page-{{ $lastPage.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-image-line text-blue-600"></i>
|
|
<span class="page-indicator text-sm font-bold text-slate-600 bg-blue-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $lastPage.PageNumber }}">{{ $lastPage.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="single-page bg-white p-4 rounded-lg border border-slate-200 hover:border-slate-300 transition-colors duration-200">
|
|
<img src="{{ $lastPage.ImagePath }}"
|
|
alt="Seite {{ $lastPage.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $lastPage.PageNumber }}, false)"
|
|
data-page="{{ $lastPage.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
</div>
|
|
{{ end }}
|
|
|
|
<!-- Beilage Pages -->
|
|
{{ range $beilageNum, $beilagePages := $images.AdditionalPages }}
|
|
{{ if $beilagePages }}
|
|
<div class="space-y-6 mt-12 pt-8 border-t-2 border-amber-200">
|
|
<!-- Header for beilage -->
|
|
<div class="flex items-center gap-3 mb-6">
|
|
<i class="ri-attachment-line text-2xl text-amber-600"></i>
|
|
<h2 class="text-xl font-semibold text-slate-800">Beilage {{ $beilageNum }}</h2>
|
|
</div>
|
|
{{ $pageCount := len $beilagePages }}
|
|
|
|
<!-- First page of beilage -->
|
|
{{ if ge $pageCount 1 }}
|
|
{{ $firstPage := index $beilagePages 0 }}
|
|
{{ if $firstPage.Available }}
|
|
<div class="newspaper-page-container" id="beilage-{{ $beilageNum }}-page-{{ $firstPage.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-image-line text-amber-600"></i>
|
|
<span class="page-indicator page-number-inhalts text-sm font-bold text-slate-600 bg-amber-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $firstPage.PageNumber }}" data-page-number="{{ $firstPage.PageNumber }}">{{ $firstPage.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="single-page bg-white p-4 rounded-lg border border-amber-200 hover:border-amber-300 transition-colors duration-200">
|
|
<img src="{{ $firstPage.ImagePath }}"
|
|
alt="Beilage {{ $beilageNum }}, Seite {{ $firstPage.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $firstPage.PageNumber }}, false)"
|
|
data-page="{{ $firstPage.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
|
|
<!-- If beilage has 4+ pages, show middle spread -->
|
|
{{ if ge $pageCount 4 }}
|
|
{{ $middlePage1 := index $beilagePages 1 }}
|
|
{{ $middlePage2 := index $beilagePages 2 }}
|
|
{{ if and $middlePage1.Available $middlePage2.Available }}
|
|
<div class="newspaper-page-container" id="beilage-{{ $beilageNum }}-page-{{ $middlePage1.PageNumber }}-{{ $middlePage2.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-copy-2-line text-amber-600 text-sm"></i>
|
|
<span class="page-indicator text-sm font-bold text-slate-600 bg-amber-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $middlePage1.PageNumber }}">{{ $middlePage1.PageNumber }}-{{ $middlePage2.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="double-spread bg-white p-4 rounded-lg border border-amber-200 hover:border-amber-300 transition-colors duration-200">
|
|
<img src="{{ $middlePage1.ImagePath }}"
|
|
alt="Beilage {{ $beilageNum }}, Seite {{ $middlePage1.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $middlePage1.PageNumber }}, true)"
|
|
data-page="{{ $middlePage1.PageNumber }}"
|
|
loading="lazy">
|
|
<img src="{{ $middlePage2.ImagePath }}"
|
|
alt="Beilage {{ $beilageNum }}, Seite {{ $middlePage2.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $middlePage2.PageNumber }}, true)"
|
|
data-page="{{ $middlePage2.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ else if eq $pageCount 2 }}
|
|
<!-- If only 2 pages, show as spread -->
|
|
{{ $page2 := index $beilagePages 1 }}
|
|
{{ if $page2.Available }}
|
|
<div class="newspaper-page-container" id="beilage-{{ $beilageNum }}-page-{{ $page2.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-image-line text-amber-600"></i>
|
|
<span class="page-indicator text-sm font-bold text-slate-600 bg-amber-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $page2.PageNumber }}">{{ $page2.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="single-page bg-white p-4 rounded-lg border border-amber-200 hover:border-amber-300 transition-colors duration-200">
|
|
<img src="{{ $page2.ImagePath }}"
|
|
alt="Beilage {{ $beilageNum }}, Seite {{ $page2.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $page2.PageNumber }}, false)"
|
|
data-page="{{ $page2.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
|
|
<!-- Last page of beilage (if 4+ pages) -->
|
|
{{ if ge $pageCount 4 }}
|
|
{{ $lastPage := index $beilagePages 3 }}
|
|
{{ if $lastPage.Available }}
|
|
<div class="newspaper-page-container" id="beilage-{{ $beilageNum }}-page-{{ $lastPage.PageNumber }}">
|
|
<div class="mb-3">
|
|
<div class="flex items-center gap-2 mb-2">
|
|
<i class="ri-file-image-line text-amber-600"></i>
|
|
<span class="page-indicator text-sm font-bold text-slate-600 bg-amber-50 px-2 py-1 rounded transition-all duration-300" data-page="{{ $lastPage.PageNumber }}">{{ $lastPage.PageNumber }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="single-page bg-white p-4 rounded-lg border border-amber-200 hover:border-amber-300 transition-colors duration-200">
|
|
<img src="{{ $lastPage.ImagePath }}"
|
|
alt="Beilage {{ $beilageNum }}, Seite {{ $lastPage.PageNumber }}"
|
|
class="newspaper-page-image cursor-pointer rounded-md hover:scale-[1.02] transition-transform duration-200"
|
|
onclick="enlargePage(this, {{ $lastPage.PageNumber }}, false)"
|
|
data-page="{{ $lastPage.PageNumber }}"
|
|
loading="lazy">
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
</div>
|
|
{{ end }}
|
|
{{ end }}
|
|
</div>
|
|
|
|
<!-- Modal for enlarged view - positioned within newspaper content -->
|
|
<div id="pageModal" class="absolute inset-0 bg-black bg-opacity-75 hidden z-50 flex items-center justify-center backdrop-blur-sm" onclick="closeModal()">
|
|
<div class="relative max-w-full max-h-full p-4">
|
|
<img id="modalImage" src="" alt="" class="max-w-full max-h-full object-contain rounded-lg">
|
|
<button onclick="closeModal()" class="absolute top-2 right-2 text-white bg-slate-800 bg-opacity-80 rounded-full w-10 h-10 flex items-center justify-center hover:bg-opacity-100 transition-all duration-200">
|
|
<i class="ri-close-line text-xl"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.newspaper-page-container {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.single-page {
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
.single-page .newspaper-page-image {
|
|
max-width: min(400px, 100%);
|
|
width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
.double-spread {
|
|
display: flex;
|
|
gap: 1rem;
|
|
justify-content: center;
|
|
width: 100%;
|
|
}
|
|
|
|
.double-spread .newspaper-page-image {
|
|
max-width: min(350px, 48%);
|
|
width: 100%;
|
|
height: auto;
|
|
flex: 1;
|
|
}
|
|
|
|
/* Larger screens - maximize available space */
|
|
@media (min-width: 1280px) {
|
|
.single-page .newspaper-page-image {
|
|
max-width: min(600px, 100%);
|
|
}
|
|
|
|
.double-spread .newspaper-page-image {
|
|
max-width: min(500px, 48%);
|
|
}
|
|
}
|
|
|
|
/* Very wide screens - take advantage of the full width */
|
|
@media (min-width: 1536px) {
|
|
.single-page .newspaper-page-image {
|
|
max-width: min(700px, 100%);
|
|
}
|
|
|
|
.double-spread .newspaper-page-image {
|
|
max-width: min(600px, 48%);
|
|
}
|
|
}
|
|
|
|
/* Medium screens */
|
|
@media (max-width: 1024px) {
|
|
.double-spread {
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.double-spread .newspaper-page-image {
|
|
max-width: 400px;
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/* Mobile */
|
|
@media (max-width: 640px) {
|
|
.single-page .newspaper-page-image,
|
|
.double-spread .newspaper-page-image {
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
|
|
/* Simple button hover styles */
|
|
button#prevPageBtn:hover:not([style*="display: none"]),
|
|
button#nextPageBtn:hover,
|
|
button#beilageBtn:hover {
|
|
background-color: rgb(209 213 219) !important; /* gray-300 */
|
|
color: rgb(55 65 81) !important; /* gray-700 */
|
|
}
|
|
|
|
button#beilageBtn:hover {
|
|
background-color: rgb(254 215 170) !important; /* amber-200 */
|
|
color: rgb(146 64 14) !important; /* amber-800 */
|
|
}
|
|
|
|
</style>
|
|
|
|
<script>
|
|
// Page highlighting state - use window to avoid redeclaration
|
|
window.highlightObserver = window.highlightObserver || null;
|
|
|
|
// Initialize or reinitialize page highlighting - can be called multiple times
|
|
function initializePageHighlighting() {
|
|
// Clean up existing observer
|
|
if (window.highlightObserver) {
|
|
window.highlightObserver.disconnect();
|
|
window.highlightObserver = null;
|
|
}
|
|
|
|
// Get all page containers
|
|
const pageContainers = document.querySelectorAll('.newspaper-page-container');
|
|
|
|
// Set up intersection observer for active page tracking
|
|
window.highlightObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
if (entry.isIntersecting) {
|
|
// Check if this is a double-spread container
|
|
const doubleSpread = entry.target.querySelector('.double-spread');
|
|
if (doubleSpread) {
|
|
// Handle double-spread: highlight both pages
|
|
const pageImages = entry.target.querySelectorAll('img[data-page]');
|
|
const pageNumbers = Array.from(pageImages).map(img => img.getAttribute('data-page'));
|
|
markCurrentPagesInInhaltsverzeichnis(pageNumbers);
|
|
} else {
|
|
// Handle single page
|
|
const pageImg = entry.target.querySelector('img[data-page]');
|
|
if (pageImg) {
|
|
const pageNumber = pageImg.getAttribute('data-page');
|
|
markCurrentPageInInhaltsverzeichnis(pageNumber);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}, {
|
|
rootMargin: '-20% 0px -70% 0px' // Trigger when page is mostly in view
|
|
});
|
|
|
|
// Observe all page containers
|
|
pageContainers.forEach(container => {
|
|
window.highlightObserver.observe(container);
|
|
});
|
|
}
|
|
|
|
// Initialize highlighting on page load
|
|
document.addEventListener('DOMContentLoaded', initializePageHighlighting);
|
|
|
|
// More comprehensive HTMX event handling for highlighting
|
|
document.body.addEventListener('htmx:afterSwap', function(event) {
|
|
console.log('HTMX afterSwap detected, target:', event.detail.target);
|
|
console.log('Target ID:', event.detail.target.id);
|
|
console.log('Target classes:', event.detail.target.className);
|
|
|
|
// Reinitialize on any content swap that might affect our page
|
|
setTimeout(() => {
|
|
console.log('Reinitializing page highlighting...');
|
|
initializePageHighlighting();
|
|
}, 100);
|
|
});
|
|
|
|
document.body.addEventListener('htmx:afterSettle', function(event) {
|
|
console.log('HTMX afterSettle detected');
|
|
setTimeout(() => {
|
|
initializePageHighlighting();
|
|
}, 200);
|
|
});
|
|
|
|
// Also try htmx:load event
|
|
document.body.addEventListener('htmx:load', function(event) {
|
|
console.log('HTMX load detected');
|
|
setTimeout(() => {
|
|
initializePageHighlighting();
|
|
}, 100);
|
|
});
|
|
|
|
function markCurrentPageInInhaltsverzeichnis(pageNumber) {
|
|
markCurrentPagesInInhaltsverzeichnis([pageNumber]);
|
|
}
|
|
|
|
function markCurrentPagesInInhaltsverzeichnis(pageNumbers) {
|
|
// Reset all page numbers in Inhaltsverzeichnis
|
|
document.querySelectorAll('.page-number-inhalts').forEach(pageNum => {
|
|
pageNum.classList.remove('text-red-600', 'font-bold');
|
|
pageNum.classList.add('text-slate-700', 'font-semibold');
|
|
// Keep original background colors
|
|
if (!pageNum.classList.contains('bg-amber-50') && !pageNum.classList.contains('bg-blue-50')) {
|
|
pageNum.classList.add('bg-blue-50');
|
|
}
|
|
});
|
|
|
|
// Find and highlight the current page numbers
|
|
const highlightedElements = [];
|
|
const highlightedRanges = new Set(); // Track which ranges we've already highlighted
|
|
|
|
pageNumbers.forEach(pageNumber => {
|
|
// Look for all entries that should be highlighted for this page
|
|
const allPageNumbers = document.querySelectorAll('.page-number-inhalts');
|
|
|
|
for (const pageNumElement of allPageNumbers) {
|
|
const startPage = parseInt(pageNumElement.getAttribute('data-page-number'));
|
|
const endPage = parseInt(pageNumElement.getAttribute('data-end-page'));
|
|
const rangeKey = `${startPage}-${endPage}`;
|
|
|
|
// Check if this page falls within this range
|
|
if (pageNumber >= startPage && pageNumber <= endPage) {
|
|
// Only highlight this range once, even if multiple visible pages fall within it
|
|
if (!highlightedRanges.has(rangeKey)) {
|
|
pageNumElement.classList.remove('text-slate-700');
|
|
pageNumElement.classList.add('text-red-600', 'font-bold');
|
|
highlightedElements.push(pageNumElement);
|
|
highlightedRanges.add(rangeKey);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Auto-scroll to first highlighted element if it exists
|
|
if (highlightedElements.length > 0) {
|
|
scrollToHighlightedPage(highlightedElements[0]);
|
|
}
|
|
|
|
// Also highlight page indicators
|
|
document.querySelectorAll('.page-indicator').forEach(indicator => {
|
|
indicator.classList.remove('text-red-600', 'font-bold');
|
|
indicator.classList.add('text-slate-600', 'font-semibold');
|
|
// Keep original backgrounds
|
|
if (!indicator.classList.contains('bg-amber-50')) {
|
|
indicator.classList.add('bg-blue-50');
|
|
}
|
|
});
|
|
|
|
// Highlight page indicators for all current pages
|
|
pageNumbers.forEach(pageNumber => {
|
|
const pageIndicator = document.querySelector(`.page-indicator[data-page="${pageNumber}"]`);
|
|
if (pageIndicator) {
|
|
pageIndicator.classList.remove('text-slate-600');
|
|
pageIndicator.classList.add('text-red-600', 'font-bold');
|
|
}
|
|
});
|
|
}
|
|
|
|
function scrollToHighlightedPage(element) {
|
|
// Check if the element is in a scrollable container
|
|
const inhaltsContainer = element.closest('.lg\\:overflow-y-auto');
|
|
if (inhaltsContainer) {
|
|
// Calculate position
|
|
const containerRect = inhaltsContainer.getBoundingClientRect();
|
|
const elementRect = element.getBoundingClientRect();
|
|
|
|
// Check if element is not fully visible
|
|
const isAboveContainer = elementRect.top < containerRect.top;
|
|
const isBelowContainer = elementRect.bottom > containerRect.bottom;
|
|
|
|
if (isAboveContainer || isBelowContainer) {
|
|
// Scroll to make element visible with some padding
|
|
element.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'center'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function enlargePage(imgElement, pageNumber, isFromSpread) {
|
|
const modal = document.getElementById('pageModal');
|
|
const modalImage = document.getElementById('modalImage');
|
|
|
|
modalImage.src = imgElement.src;
|
|
modalImage.alt = imgElement.alt;
|
|
|
|
modal.classList.remove('hidden');
|
|
|
|
// Mark current page when enlarged
|
|
markCurrentPageInInhaltsverzeichnis(pageNumber);
|
|
}
|
|
|
|
function closeModal() {
|
|
const modal = document.getElementById('pageModal');
|
|
modal.classList.add('hidden');
|
|
}
|
|
|
|
// Close modal on Escape key
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
closeModal();
|
|
}
|
|
});
|
|
|
|
// Navigation functions and state management - use window to avoid redeclaration
|
|
window.currentPageContainers = window.currentPageContainers || [];
|
|
window.currentActiveIndex = window.currentActiveIndex || 0;
|
|
window.pageObserver = window.pageObserver || null;
|
|
|
|
// Initialize or reinitialize page tracking - can be called multiple times
|
|
function initializePageTracking() {
|
|
// Clean up existing observer
|
|
if (window.pageObserver) {
|
|
window.pageObserver.disconnect();
|
|
window.pageObserver = null;
|
|
}
|
|
|
|
// Reset state
|
|
window.currentPageContainers = Array.from(document.querySelectorAll('.newspaper-page-container'));
|
|
window.currentActiveIndex = 0;
|
|
updateButtonStates();
|
|
|
|
// Set up new observer
|
|
const existingObserver = document.querySelector('.newspaper-page-container');
|
|
if (existingObserver) {
|
|
let visibleContainers = new Set();
|
|
|
|
window.pageObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach((entry) => {
|
|
const containerIndex = window.currentPageContainers.indexOf(entry.target);
|
|
if (containerIndex !== -1) {
|
|
if (entry.isIntersecting) {
|
|
visibleContainers.add(containerIndex);
|
|
} else {
|
|
visibleContainers.delete(containerIndex);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Update currentActiveIndex to the first (topmost) visible container
|
|
if (visibleContainers.size > 0) {
|
|
const sortedVisible = Array.from(visibleContainers).sort((a, b) => a - b);
|
|
const newActiveIndex = sortedVisible[0];
|
|
if (newActiveIndex !== window.currentActiveIndex) {
|
|
window.currentActiveIndex = newActiveIndex;
|
|
updateButtonStates();
|
|
}
|
|
}
|
|
}, {
|
|
rootMargin: '-20% 0px -70% 0px'
|
|
});
|
|
|
|
window.currentPageContainers.forEach(container => {
|
|
window.pageObserver.observe(container);
|
|
});
|
|
}
|
|
}
|
|
|
|
// Initialize on page load
|
|
document.addEventListener('DOMContentLoaded', initializePageTracking);
|
|
|
|
// More comprehensive HTMX event handling for navigation tracking
|
|
document.body.addEventListener('htmx:afterSwap', function(event) {
|
|
console.log('HTMX afterSwap for navigation tracking');
|
|
setTimeout(() => {
|
|
console.log('Reinitializing page tracking...');
|
|
initializePageTracking();
|
|
}, 100);
|
|
});
|
|
|
|
document.body.addEventListener('htmx:afterSettle', function(event) {
|
|
console.log('HTMX afterSettle for navigation tracking');
|
|
setTimeout(() => {
|
|
initializePageTracking();
|
|
}, 200);
|
|
});
|
|
|
|
document.body.addEventListener('htmx:load', function(event) {
|
|
console.log('HTMX load for navigation tracking');
|
|
setTimeout(() => {
|
|
initializePageTracking();
|
|
}, 100);
|
|
});
|
|
|
|
function scrollToPreviousPage() {
|
|
if (window.currentActiveIndex > 0) {
|
|
// Move to previous container that's not currently visible
|
|
let targetIndex = window.currentActiveIndex - 1;
|
|
|
|
// Ensure we go to the previous non-visible container
|
|
window.currentActiveIndex = targetIndex;
|
|
window.currentPageContainers[window.currentActiveIndex].scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
|
|
// Update button states after a brief delay to let intersection observer catch up
|
|
setTimeout(() => {
|
|
updateButtonStates();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
function scrollToNextPage() {
|
|
if (window.currentActiveIndex < window.currentPageContainers.length - 1) {
|
|
// Move to next container that's not currently visible
|
|
let targetIndex = window.currentActiveIndex + 1;
|
|
|
|
// Ensure we go to the next non-visible container
|
|
window.currentActiveIndex = targetIndex;
|
|
window.currentPageContainers[window.currentActiveIndex].scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
|
|
// Update button states after a brief delay to let intersection observer catch up
|
|
setTimeout(() => {
|
|
updateButtonStates();
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
function scrollToBeilage() {
|
|
// Find the first beilage container
|
|
const beilageContainer = document.querySelector('[class*="border-t-2 border-amber-200"]');
|
|
if (beilageContainer) {
|
|
beilageContainer.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateButtonStates() {
|
|
const prevBtn = document.getElementById('prevPageBtn');
|
|
const nextBtn = document.getElementById('nextPageBtn');
|
|
|
|
if (prevBtn) {
|
|
if (window.currentActiveIndex <= 0) {
|
|
prevBtn.style.display = 'none';
|
|
} else {
|
|
prevBtn.style.display = 'flex';
|
|
}
|
|
}
|
|
|
|
if (nextBtn) {
|
|
if (window.currentActiveIndex >= window.currentPageContainers.length - 1) {
|
|
nextBtn.style.display = 'none';
|
|
} else {
|
|
nextBtn.style.display = 'flex';
|
|
}
|
|
}
|
|
}
|
|
|
|
function shareCurrentPage() {
|
|
const button = document.getElementById('shareLinkBtn');
|
|
|
|
// Get current page information
|
|
let pageInfo = '';
|
|
|
|
// Try to get the currently visible page number from active containers
|
|
if (window.currentActiveIndex !== undefined && window.currentPageContainers && window.currentPageContainers[window.currentActiveIndex]) {
|
|
const activeContainer = window.currentPageContainers[window.currentActiveIndex];
|
|
const pageElement = activeContainer.querySelector('[data-page]');
|
|
if (pageElement) {
|
|
const pageNumber = pageElement.getAttribute('data-page');
|
|
pageInfo = `#page-${pageNumber}`;
|
|
}
|
|
}
|
|
|
|
// Construct the shareable URL
|
|
const currentUrl = window.location.origin + window.location.pathname + pageInfo;
|
|
|
|
// Try to use Web Share API if available (mobile browsers)
|
|
if (navigator.share) {
|
|
navigator.share({
|
|
title: document.title,
|
|
url: currentUrl
|
|
}).catch(err => {
|
|
console.log('Error sharing:', err);
|
|
// Fallback to clipboard
|
|
copyToClipboard(currentUrl, button);
|
|
});
|
|
} else {
|
|
// Fallback: copy to clipboard
|
|
copyToClipboard(currentUrl, button);
|
|
}
|
|
}
|
|
|
|
function copyToClipboard(text, button) {
|
|
if (navigator.clipboard) {
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
// Show temporary notification
|
|
showNotification('Link kopiert!', 'success', button);
|
|
}).catch(err => {
|
|
console.error('Failed to copy:', err);
|
|
showNotification('Kopieren fehlgeschlagen', 'error', button);
|
|
});
|
|
} else {
|
|
// Fallback for older browsers
|
|
const textarea = document.createElement('textarea');
|
|
textarea.value = text;
|
|
document.body.appendChild(textarea);
|
|
textarea.select();
|
|
try {
|
|
document.execCommand('copy');
|
|
showNotification('Link kopiert!', 'success', button);
|
|
} catch (err) {
|
|
console.error('Fallback copy failed:', err);
|
|
showNotification('Kopieren fehlgeschlagen', 'error', button);
|
|
}
|
|
document.body.removeChild(textarea);
|
|
}
|
|
}
|
|
|
|
function generateCitation() {
|
|
const button = document.getElementById('citationBtn');
|
|
|
|
// Get current page and issue information
|
|
const issueInfo = document.title || 'KGPZ';
|
|
const currentUrl = window.location.href;
|
|
|
|
// Basic citation format (can be expanded later)
|
|
const currentDate = new Date().toLocaleDateString('de-DE');
|
|
const citation = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${issueInfo}. Digital verfügbar unter: ${currentUrl} (Zugriff: ${currentDate}).`;
|
|
|
|
// Copy citation to clipboard
|
|
copyToClipboard(citation, button);
|
|
}
|
|
|
|
function showNotification(message, type = 'success', button) {
|
|
// Remove any existing notifications
|
|
const existingNotification = document.getElementById('notification');
|
|
if (existingNotification) {
|
|
existingNotification.remove();
|
|
}
|
|
|
|
// Create notification element
|
|
const notification = document.createElement('div');
|
|
notification.id = 'notification';
|
|
notification.className = `fixed px-3 py-2 rounded-md text-white text-sm font-medium z-50 transition-opacity duration-300 ${
|
|
type === 'success' ? 'bg-green-500' : 'bg-red-500'
|
|
}`;
|
|
notification.textContent = message;
|
|
|
|
// Position notification next to button if button is provided
|
|
if (button) {
|
|
const buttonRect = button.getBoundingClientRect();
|
|
notification.style.left = `${buttonRect.left - 80}px`; // Position to the left of button
|
|
notification.style.top = `${buttonRect.top + buttonRect.height / 2 - 20}px`; // Center vertically with button
|
|
} else {
|
|
// Fallback to top-right corner
|
|
notification.className += ' top-4 right-4';
|
|
}
|
|
|
|
// Add to page
|
|
document.body.appendChild(notification);
|
|
|
|
// Auto-remove after 3 seconds
|
|
setTimeout(() => {
|
|
notification.style.opacity = '0';
|
|
setTimeout(() => {
|
|
notification.remove();
|
|
}, 300);
|
|
}, 3000);
|
|
}
|
|
</script> |