Files
kgpz_web/views/routes/ausgabe/components/_newspaper_layout.gohtml
2025-09-15 12:57:42 +02:00

973 lines
33 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 }}" data-pages="{{ $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 text-sm font-bold text-slate-600 bg-amber-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-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 }}" data-pages="{{ $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) {
console.log('markCurrentPagesInInhaltsverzeichnis called with:', pageNumbers);
// Reset all page container borders to default
document.querySelectorAll('[data-page-container]').forEach(container => {
if (container.hasAttribute('data-beilage')) {
container.classList.remove('border-red-500');
container.classList.add('border-amber-400');
} else {
container.classList.remove('border-red-500');
container.classList.add('border-slate-300');
}
});
// Hide all continuation entries by default
document.querySelectorAll('.continuation-entry').forEach(entry => {
entry.classList.add('hidden');
});
// 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');
pageNum.style.textDecoration = '';
pageNum.style.pointerEvents = '';
// Restore hover effects
if (pageNum.classList.contains('bg-blue-50')) {
pageNum.classList.add('hover:bg-blue-100');
} else if (pageNum.classList.contains('bg-amber-50')) {
pageNum.classList.add('hover:bg-amber-100');
}
// Keep original background colors
if (!pageNum.classList.contains('bg-amber-50') && !pageNum.classList.contains('bg-blue-50')) {
pageNum.classList.add('bg-blue-50');
}
});
// Reset all containers and links in Inhaltsverzeichnis
document.querySelectorAll('.inhalts-entry').forEach(container => {
container.classList.add('hover:bg-slate-100');
container.style.cursor = '';
});
document.querySelectorAll('.inhalts-entry .author-link').forEach(link => {
link.style.textDecoration = '';
link.style.pointerEvents = '';
link.classList.remove('no-underline');
});
document.querySelectorAll('.inhalts-entry a[href*="/"]').forEach(link => {
link.style.textDecoration = '';
link.style.pointerEvents = '';
link.classList.remove('no-underline');
if (link.classList.contains('bg-blue-50')) {
link.classList.add('hover:bg-blue-100');
}
});
// Find and highlight the current page numbers
const highlightedElements = [];
const highlightedRanges = new Set(); // Track which ranges we've already highlighted
pageNumbers.forEach(pageNumber => {
// Convert pageNumber to integer for comparison
const currentPageNum = parseInt(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 (currentPageNum >= startPage && currentPageNum <= endPage) {
// Only highlight this range once, even if multiple visible pages fall within it
if (!highlightedRanges.has(rangeKey)) {
pageNumElement.classList.remove('text-slate-700', 'hover:bg-blue-100', 'hover:bg-amber-100');
pageNumElement.classList.add('text-red-600', 'font-bold');
pageNumElement.style.textDecoration = 'none';
pageNumElement.style.pointerEvents = 'none';
highlightedElements.push(pageNumElement);
highlightedRanges.add(rangeKey);
// Highlight the page container's left border
const pageContainer = document.querySelector(`[data-page-container="${startPage}"]`);
if (pageContainer) {
pageContainer.classList.remove('border-slate-300', 'border-amber-400');
pageContainer.classList.add('border-red-500');
// Show continuation entries for this visible page
const continuationEntries = pageContainer.querySelectorAll('.continuation-entry[data-page="' + startPage + '"]');
continuationEntries.forEach(entry => {
entry.classList.remove('hidden');
});
}
// Also make links in the current article non-clickable and remove hover effects
const parentEntry = pageNumElement.closest('.mb-4');
if (parentEntry) {
// Remove hover effects from the container
const entryContainers = parentEntry.querySelectorAll('.inhalts-entry');
entryContainers.forEach(container => {
container.classList.remove('hover:bg-slate-100');
container.style.cursor = 'default';
});
// Make all links non-clickable and remove underlines
parentEntry.querySelectorAll('.author-link').forEach(link => {
link.style.textDecoration = 'none';
link.style.pointerEvents = 'none';
link.classList.add('no-underline');
});
// Also handle issue reference links
parentEntry.querySelectorAll('a[href*="/"]').forEach(link => {
if (link.getAttribute('aria-current') === 'page') {
link.style.textDecoration = 'none';
link.style.pointerEvents = 'none';
link.classList.add('no-underline');
link.classList.remove('hover:bg-blue-100');
}
});
}
}
}
}
});
// 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 => {
// 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(() => {
showSimplePopup(button, 'Link kopiert!');
}).catch(err => {
showSimplePopup(button, 'Kopieren fehlgeschlagen');
});
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
showSimplePopup(button, successful ? 'Link kopiert!' : 'Kopieren fehlgeschlagen');
} catch (err) {
showSimplePopup(button, 'Kopieren fehlgeschlagen');
} finally {
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 to clipboard
if (navigator.clipboard) {
navigator.clipboard.writeText(citation).then(() => {
showSimplePopup(button, 'Zitation kopiert!');
}).catch(err => {
showSimplePopup(button, 'Kopieren fehlgeschlagen');
});
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = citation;
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
showSimplePopup(button, successful ? 'Zitation kopiert!' : 'Kopieren fehlgeschlagen');
} catch (err) {
showSimplePopup(button, 'Kopieren fehlgeschlagen');
} finally {
document.body.removeChild(textarea);
}
}
}
function showSimplePopup(button, message) {
// Remove any existing popup
const existingPopup = document.querySelector('.simple-popup');
if (existingPopup) {
existingPopup.remove();
}
// Create popup element
const popup = document.createElement('div');
popup.className = 'simple-popup';
popup.textContent = message;
// Style the popup
popup.style.cssText = `
position: fixed;
background: #374151;
color: white;
padding: 6px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
z-index: 1000;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
white-space: nowrap;
`;
// Position popup next to button
const buttonRect = button.getBoundingClientRect();
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
popup.style.left = (buttonRect.left + scrollLeft - 10) + 'px';
popup.style.top = (buttonRect.bottom + scrollTop + 8) + 'px';
// Add to page
document.body.appendChild(popup);
// Fade in
setTimeout(() => {
popup.style.opacity = '1';
}, 10);
// Auto-remove after 2 seconds
setTimeout(() => {
popup.style.opacity = '0';
setTimeout(() => {
if (popup.parentNode) {
popup.remove();
}
}, 200);
}, 2000);
}
// Handle hash navigation to scroll to specific pages
function scrollToPageFromHash() {
const hash = window.location.hash;
let pageNumber = '';
let targetContainer = null;
if (hash.startsWith('#page-')) {
pageNumber = hash.replace('#page-', '');
// Try to find exact page container first
targetContainer = document.getElementById(`page-${pageNumber}`);
// If not found, try to find container that contains this page
if (!targetContainer) {
// Look for double-spread containers that contain this page
const containers = document.querySelectorAll('.newspaper-page-container[data-pages]');
for (const container of containers) {
const pages = container.getAttribute('data-pages');
if (pages && pages.split(',').includes(pageNumber)) {
targetContainer = container;
break;
}
}
}
// If still not found, try beilage containers
if (!targetContainer) {
targetContainer = document.getElementById(`beilage-1-page-${pageNumber}`) ||
document.getElementById(`beilage-2-page-${pageNumber}`) ||
document.querySelector(`[id*="beilage"][id*="page-${pageNumber}"]`);
}
} else if (hash.startsWith('#beilage-')) {
// Handle beilage-specific hashes like #beilage-1-page-101
const match = hash.match(/#beilage-(\d+)-page-(\d+)/);
if (match) {
const beilageNum = match[1];
pageNumber = match[2];
targetContainer = document.getElementById(`beilage-${beilageNum}-page-${pageNumber}`);
}
}
if (targetContainer && pageNumber) {
setTimeout(() => {
targetContainer.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
// Highlight the specific page in the table of contents
markCurrentPageInInhaltsverzeichnis(pageNumber);
}, 300);
}
}
// Function to copy page permalink
function copyPagePermalink(pageNumber, button, isBeilage = false) {
let pageFragment = '';
if (isBeilage) {
pageFragment = `#beilage-1-page-${pageNumber}`;
} else {
pageFragment = `#page-${pageNumber}`;
}
const currentUrl = window.location.origin + window.location.pathname + pageFragment;
// Copy to clipboard
if (navigator.clipboard) {
navigator.clipboard.writeText(currentUrl).then(() => {
showSimplePopup(button, 'Link kopiert!');
}).catch(err => {
showSimplePopup(button, 'Kopieren fehlgeschlagen');
});
} else {
// Fallback for older browsers
const textarea = document.createElement('textarea');
textarea.value = currentUrl;
document.body.appendChild(textarea);
textarea.select();
try {
const successful = document.execCommand('copy');
showSimplePopup(button, successful ? 'Link kopiert!' : 'Kopieren fehlgeschlagen');
} catch (err) {
showSimplePopup(button, 'Kopieren fehlgeschlagen');
} finally {
document.body.removeChild(textarea);
}
}
}
// Initialize hash handling
document.addEventListener('DOMContentLoaded', scrollToPageFromHash);
window.addEventListener('hashchange', scrollToPageFromHash);
</script>