mirror of
				https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
				synced 2025-10-31 01:55:29 +00:00 
			
		
		
		
	count ontinuations
This commit is contained in:
		| @@ -1,41 +0,0 @@ | ||||
| {{ $model := .model }} | ||||
| {{ $images := $model.Images }} | ||||
| {{ if $images.HasImages }} | ||||
| <div class="mt-6"> | ||||
| 	<h3 class="text-lg font-medium mb-4">Seiten der Ausgabe</h3> | ||||
|  | ||||
| 	{{- if $images.MainPages }} | ||||
| 	<div class="mb-6"> | ||||
| 		<h4 class="text-md font-medium mb-2">Hauptausgabe</h4> | ||||
| 		<div class="grid grid-cols-2 gap-4"> | ||||
| 			{{- range $images.MainPages -}} | ||||
| 				{{- if .Available -}} | ||||
| 				<div class="border rounded-lg p-2"> | ||||
| 					<h5 class="font-bold text-slate-700 bg-blue-50 px-2 py-1 rounded text-sm mb-2">Seite {{ .PageNumber }}</h5> | ||||
| 					<img src="{{ .ImagePath }}" alt="Seite {{ .PageNumber }}" class="w-full h-auto border" loading="lazy"> | ||||
| 				</div> | ||||
| 				{{- end -}} | ||||
| 			{{- end -}} | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	{{- end }} | ||||
|  | ||||
| 	{{- range $beilageNum, $pages := $images.AdditionalPages -}} | ||||
| 		{{- if $pages }} | ||||
| 		<div class="mb-6"> | ||||
| 			<h4 class="text-md font-medium mb-2">Beilage {{ $beilageNum }}</h4> | ||||
| 			<div class="grid grid-cols-2 gap-4"> | ||||
| 				{{- range $pages -}} | ||||
| 					{{- if .Available -}} | ||||
| 					<div class="border rounded-lg p-2"> | ||||
| 						<h5 class="font-bold text-slate-700 bg-blue-50 px-2 py-1 rounded text-sm mb-2">Seite {{ .PageNumber }}</h5> | ||||
| 						<img src="{{ .ImagePath }}" alt="Beilage {{ $beilageNum }}, Seite {{ .PageNumber }}" class="w-full h-auto border" loading="lazy"> | ||||
| 					</div> | ||||
| 					{{- end -}} | ||||
| 				{{- end -}} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		{{- end -}} | ||||
| 	{{- end -}} | ||||
| </div> | ||||
| {{ end }} | ||||
| @@ -1,579 +0,0 @@ | ||||
| {{ $model := .model }} | ||||
|  | ||||
| <div class="w-full min-h-screen"> | ||||
| 	<!-- Navigation Bar --> | ||||
| 	<div class="sticky top-0 z-40 bg-white border-b border-gray-200 shadow-sm mb-6"> | ||||
| 		<div class="max-w-7xl mx-auto px-4 py-4"> | ||||
| 			<div class="flex items-center justify-between"> | ||||
| 				<div class="flex items-center space-x-4"> | ||||
| 					{{ template "_title_nav" . }} | ||||
| 				</div> | ||||
| 				<div class="flex items-center space-x-3"> | ||||
| 					<!-- Navigation buttons --> | ||||
| 					<button onclick="scrollToPreviousPage()" class="flex items-center px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors"> | ||||
| 						<i class="ri-arrow-up-line mr-1"></i> | ||||
| 						Vorherige Seite | ||||
| 					</button> | ||||
| 					<button onclick="scrollToNextPage()" class="flex items-center px-3 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors"> | ||||
| 						<i class="ri-arrow-down-line mr-1"></i> | ||||
| 						Nächste Seite | ||||
| 					</button> | ||||
| 					{{ if $model.Images.AdditionalPages }} | ||||
| 						<button onclick="scrollToBeilage()" class="flex items-center px-3 py-2 bg-amber-600 hover:bg-amber-700 text-white rounded-md transition-colors"> | ||||
| 							<i class="ri-attachment-line mr-1"></i> | ||||
| 							Zu Beilage | ||||
| 						</button> | ||||
| 					{{ end }} | ||||
| 					<!-- Switch back to sidebar layout --> | ||||
| 					<a href="?layout=sidebar" class="flex items-center px-3 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-md transition-colors"> | ||||
| 						<i class="ri-sidebar-unfold-line mr-1"></i> | ||||
| 						Seitenleiste | ||||
| 					</a> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | ||||
| 	<div class="max-w-7xl mx-auto px-4"> | ||||
| 		<div class="flex flex-col lg:flex-row gap-6 w-full"> | ||||
| 			<!-- Left side: Collapsible Inhaltsverzeichnis --> | ||||
| 			<div class="lg:w-1/4 xl:w-1/5 flex-shrink-0"> | ||||
| 				<div class="lg:sticky lg:top-24 lg:max-h-[calc(100vh-6rem)] lg:overflow-y-auto"> | ||||
| 					{{ template "_inhaltsverzeichnis" . }} | ||||
| 				</div> | ||||
| 			</div> | ||||
|  | ||||
| 			<!-- Right side: Full-width Newspaper pages --> | ||||
| 			<div class="lg:w-3/4 xl:w-4/5 flex-1"> | ||||
| 				{{ template "_newspaper_layout" . }} | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
|  | ||||
| <script> | ||||
| let currentPageContainers = []; | ||||
| let currentActiveIndex = 0; | ||||
|  | ||||
| // Initialize page tracking for full-width layout | ||||
| document.addEventListener('DOMContentLoaded', function() { | ||||
| 	// Get all page containers | ||||
| 	currentPageContainers = document.querySelectorAll('.newspaper-page-container'); | ||||
|  | ||||
| 	// Set up intersection observer for active page tracking | ||||
| 	const observer = 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); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// Update current active index | ||||
| 				const containerIndex = Array.from(currentPageContainers).indexOf(entry.target); | ||||
| 				if (containerIndex !== -1) { | ||||
| 					currentActiveIndex = containerIndex; | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	}, { | ||||
| 		rootMargin: '-20% 0px -70% 0px' // Trigger when page is mostly in view | ||||
| 	}); | ||||
|  | ||||
| 	// Observe all page containers | ||||
| 	currentPageContainers.forEach(container => { | ||||
| 		observer.observe(container); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| function scrollToPreviousPage() { | ||||
| 	if (currentActiveIndex > 0) { | ||||
| 		currentActiveIndex--; | ||||
| 		currentPageContainers[currentActiveIndex].scrollIntoView({ | ||||
| 			behavior: 'smooth', | ||||
| 			block: 'start' | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function scrollToNextPage() { | ||||
| 	if (currentActiveIndex < currentPageContainers.length - 1) { | ||||
| 		currentActiveIndex++; | ||||
| 		currentPageContainers[currentActiveIndex].scrollIntoView({ | ||||
| 			behavior: 'smooth', | ||||
| 			block: 'start' | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function scrollToBeilage() { | ||||
| 	const beilageElement = document.querySelector('[id^="beilage-"]'); | ||||
| 	if (beilageElement) { | ||||
| 		beilageElement.scrollIntoView({ | ||||
| 			behavior: 'smooth', | ||||
| 			block: 'start' | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| function markCurrentPageInInhaltsverzeichnis(pageNumber) { | ||||
| 	markCurrentPagesInInhaltsverzeichnis([pageNumber]); | ||||
| } | ||||
|  | ||||
| function markCurrentPagesInInhaltsverzeichnis(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('bg-red-500', 'text-white'); | ||||
| 		pageNum.classList.add('text-slate-700'); | ||||
| 		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'); | ||||
| 		} | ||||
| 		// Restore original background colors | ||||
| 		if (pageNum.classList.contains('bg-amber-50')) { | ||||
| 			// Keep amber background for Beilage pages | ||||
| 		} else { | ||||
| 			pageNum.classList.remove('bg-amber-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('bg-blue-50', 'bg-amber-50', 'text-slate-700', 'hover:bg-blue-100', 'hover:bg-amber-100'); | ||||
| 					pageNumElement.classList.add('bg-red-500', 'text-white'); | ||||
| 					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('bg-red-500', 'text-white'); | ||||
| 		indicator.classList.add('bg-blue-50', 'text-slate-600'); | ||||
| 	}); | ||||
|  | ||||
| 	// Highlight page indicators for all current pages | ||||
| 	pageNumbers.forEach(pageNumber => { | ||||
| 		const pageIndicator = document.querySelector(`.page-indicator[data-page="${pageNumber}"]`); | ||||
| 		if (pageIndicator) { | ||||
| 			pageIndicator.classList.remove('bg-blue-50', 'bg-green-50', 'bg-amber-50', 'text-slate-600'); | ||||
| 			pageIndicator.classList.add('bg-red-500', 'text-white'); | ||||
| 		} | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| 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' | ||||
| 			}); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Keyboard navigation | ||||
| document.addEventListener('keydown', function(e) { | ||||
| 	if (e.key === 'Escape') { | ||||
| 		// Close modal if open | ||||
| 		const modal = document.getElementById('pageModal'); | ||||
| 		if (modal && !modal.classList.contains('hidden')) { | ||||
| 			modal.classList.add('hidden'); | ||||
| 		} | ||||
| 	} else if (e.key === 'ArrowUp' || e.key === 'PageUp') { | ||||
| 		e.preventDefault(); | ||||
| 		scrollToPreviousPage(); | ||||
| 	} else if (e.key === 'ArrowDown' || e.key === 'PageDown') { | ||||
| 		e.preventDefault(); | ||||
| 		scrollToNextPage(); | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| 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(() => { | ||||
| 			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); | ||||
| } | ||||
|  | ||||
| // 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); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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; | ||||
| 	copyToClipboardWithMessage(currentUrl, button, 'Link kopiert!'); | ||||
| } | ||||
|  | ||||
| // Initialize hash handling | ||||
| document.addEventListener('DOMContentLoaded', scrollToPageFromHash); | ||||
| window.addEventListener('hashchange', scrollToPageFromHash); | ||||
| </script> | ||||
| @@ -13,13 +13,20 @@ | ||||
| 				{{ $firstItem := "" }} | ||||
| 				{{ if $pageItems }}{{ $firstItem = index $pageItems 0 }}{{ end }} | ||||
|  | ||||
|  | ||||
| 				<!-- Individual page entry --> | ||||
| 				<div | ||||
| 					class="mb-6 first:mb-0 pl-4 border-l-4 border-slate-300 page-entry" | ||||
| 					data-page-container="{{ $page }}"> | ||||
| 					<div class="flex items-center justify-between gap-2 mb-2"> | ||||
| 						<div class="flex items-center gap-2"> | ||||
| 							<span class="icon-container">{{ if $firstItem }}{{ PageIcon $firstItem.PageIcon }}{{ else }}{{ PageIcon "first" }}{{ end }}</span> | ||||
| 							<span class="icon-container" | ||||
| 								>{{ if $firstItem }} | ||||
| 									{{ PageIcon $firstItem.PageIcon }} | ||||
| 								{{ else }} | ||||
| 									{{ PageIcon "first" }} | ||||
| 								{{ end }}</span | ||||
| 							> | ||||
| 							<a | ||||
| 								href="#page-{{ $page }}" | ||||
| 								class="page-number-inhalts font-bold text-slate-700 bg-slate-100 px-2 py-1 rounded text-sm transition-colors duration-200 hover:bg-slate-200 no-underline" | ||||
| @@ -40,50 +47,68 @@ | ||||
| 					<div class="space-y-0"> | ||||
| 						{{ if $pageItems }} | ||||
| 							{{ range $individualPiece := $pageItems }} | ||||
| 							<div | ||||
| 								class="inhalts-entry py-1 px-0 bg-slate-50 rounded hover:bg-slate-100 transition-colors duration-200 {{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 									continuation-entry | ||||
| 								{{ end }}" | ||||
| 								data-page="{{ $page }}" | ||||
| 								{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 									data-is-continuation="true" | ||||
| 								{{ end }} | ||||
| 								style="{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 									display: none; | ||||
| 								{{ end }}"> | ||||
| 								{{ template "_inhaltsverzeichnis_eintrag" $individualPiece.PieceByIssue }} | ||||
| 								<div | ||||
| 									class="inhalts-entry py-1 px-0 bg-slate-50 rounded hover:bg-slate-100 transition-colors duration-200 {{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 										continuation-entry | ||||
| 									{{ end }}" | ||||
| 									data-page="{{ $page }}" | ||||
| 									{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 										data-is-continuation="true" | ||||
| 									{{ end }} | ||||
| 									style="{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 										display: none; | ||||
| 									{{ end }}"> | ||||
| 									{{ template "_inhaltsverzeichnis_eintrag" $individualPiece.PieceByIssue }} | ||||
|  | ||||
|  | ||||
| 								<!-- Links zu anderen Teilen: --> | ||||
| 								{{ if and (not $individualPiece.PieceByIssue.IsContinuation) (gt (len $individualPiece.IssueRefs) 1) }} | ||||
| 									<div class="mt-1 pt-1 border-t border-slate-100"> | ||||
| 										<div class="flex items-center flex-wrap gap-1"> | ||||
| 											<i class="ri-links-line text-slate-600 text-sm mr-1"></i> | ||||
| 											{{ range $issue := $individualPiece.IssueRefs }} | ||||
| 												<a | ||||
| 													href="/{{- $issue.When -}}/{{- $issue.Nr -}}{{- if $issue.Von -}} | ||||
| 														{{- if $issue.Beilage -}} | ||||
| 															#beilage-{{ $issue.Beilage }}-page-{{ $issue.Von }} | ||||
| 														{{- else -}} | ||||
| 															#page-{{ $issue.Von }} | ||||
| 														{{- end -}} | ||||
| 													{{- end -}}" | ||||
| 													class="inline-flex items-center gap-1 px-2 py-1 bg-slate-100 text-slate-700 rounded-md text-xs font-medium hover:bg-slate-200 transition-colors duration-150" | ||||
| 													{{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year | ||||
| 														$model.Datum.When.Year) | ||||
| 													-}} | ||||
| 														aria-current="page" | ||||
| 													{{ end }}> | ||||
| 													<i class="ri-calendar-line text-xs"></i> | ||||
| 													{{- $issue.When.Year }} Nr. | ||||
| 													{{ $issue.Nr -}} | ||||
| 												</a> | ||||
| 											{{ end }} | ||||
| 									<!-- Links zu anderen Teilen: --> | ||||
| 									{{ if and (not $individualPiece.PieceByIssue.IsContinuation) (gt (len $individualPiece.IssueRefs) 1) }} | ||||
| 										<div class="mt-1 pt-1 border-t border-slate-100"> | ||||
| 											<div class="flex items-center flex-wrap gap-1"> | ||||
| 												<i class="ri-links-line text-slate-600 text-sm mr-1"></i> | ||||
| 												{{ range $index, $issue := $individualPiece.IssueRefs }} | ||||
| 													<div | ||||
| 														class="inline-flex items-center gap-1 px-2 py-1 bg-slate-100 rounded-md text-xs font-medium hover:bg-slate-200 transition-colors duration-150 {{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year $model.Datum.When.Year) -}} | ||||
| 															bg-red-100 text-red-700 | ||||
| 														{{ end }}"> | ||||
| 														<span class="text-black text-xs font-bold">{{ add $index 1 }}</span> | ||||
| 														<span class="w-px h-3 bg-slate-300 mx-1"></span> | ||||
| 														<a | ||||
| 															href="/{{- $issue.When -}}/{{- $issue.Nr -}}{{- if $issue.Von -}} | ||||
| 																{{- if $issue.Beilage -}} | ||||
| 																	#beilage-{{ $issue.Beilage }}-page-{{ $issue.Von }} | ||||
| 																{{- else -}} | ||||
| 																	#page-{{ $issue.Von }} | ||||
| 																{{- end -}} | ||||
| 															{{- end -}}" | ||||
| 															class="text-slate-700 no-underline hover:text-slate-900 {{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year $model.Datum.When.Year) -}} | ||||
| 																text-red-700 pointer-events-none | ||||
| 															{{ end }}" | ||||
| 															{{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year | ||||
| 																$model.Datum.When.Year) | ||||
| 															-}} | ||||
| 																aria-current="page" | ||||
| 															{{ end }}> | ||||
| 															{{- $issueKey := printf "%d-%d" $issue.When.Year $issue.Nr -}} | ||||
| 															{{- $issueData := GetIssue $issueKey -}} | ||||
| 															{{- if $issueData -}} | ||||
| 																{{ $issueData.Datum.When.Day }}.{{ $issueData.Datum.When.Month }}.{{ $issueData.Datum.When.Year }} | ||||
| 															{{- else -}} | ||||
| 																{{ $issue.When.Year }} Nr. | ||||
| 																{{ $issue.Nr }} | ||||
| 															{{- end -}} | ||||
| 															{{- if $issue.Von }} | ||||
| 																S. | ||||
| 																{{ $issue.Von }} | ||||
| 															{{- end -}} | ||||
| 														</a> | ||||
| 													</div> | ||||
| 												{{ end }} | ||||
| 											</div> | ||||
| 										</div> | ||||
| 									</div> | ||||
| 								{{ end }} | ||||
| 							</div> | ||||
| 						{{ end }} | ||||
| 									{{ end }} | ||||
| 								</div> | ||||
| 							{{ end }} | ||||
| 						{{ else }} | ||||
| 							<!-- Empty page indicator --> | ||||
| 							<div class="inhalts-entry py-1 px-0"> | ||||
| @@ -113,10 +138,16 @@ | ||||
| 				{{ $allPages := $model.AdditionalPieces.Pages }} | ||||
| 				{{ $pageCount := len $allPages }} | ||||
| 				{{ $iconType := "first" }} | ||||
| 				{{ if eq $pageIndex 0 }}{{ $iconType = "first" }} | ||||
| 				{{ else if eq $pageIndex (sub $pageCount 1) }}{{ $iconType = "last" }} | ||||
| 				{{ else if eq (mod $pageIndex 2) 1 }}{{ $iconType = "even" }} | ||||
| 				{{ else }}{{ $iconType = "odd" }}{{ end }} | ||||
| 				{{ if eq $pageIndex 0 }} | ||||
| 					{{ $iconType = "first" }} | ||||
| 				{{ else if eq $pageIndex (sub $pageCount 1) }} | ||||
| 					{{ $iconType = "last" }} | ||||
| 				{{ else if eq (mod $pageIndex 2) 1 }} | ||||
| 					{{ $iconType = "even" }} | ||||
| 				{{ else }} | ||||
| 					{{ $iconType = "odd" }} | ||||
| 				{{ end }} | ||||
|  | ||||
|  | ||||
| 				<!-- Individual beilage page entry --> | ||||
| 				<div | ||||
| @@ -125,7 +156,13 @@ | ||||
| 					data-beilage="true"> | ||||
| 					<div class="flex items-center justify-between gap-2 mb-2"> | ||||
| 						<div class="flex items-center gap-2"> | ||||
| 							<span class="icon-container">{{ if $firstItem }}{{ PageIcon $firstItem.PageIcon }}{{ else }}{{ PageIcon $iconType }}{{ end }}</span> | ||||
| 							<span class="icon-container" | ||||
| 								>{{ if $firstItem }} | ||||
| 									{{ PageIcon $firstItem.PageIcon }} | ||||
| 								{{ else }} | ||||
| 									{{ PageIcon $iconType }} | ||||
| 								{{ end }}</span | ||||
| 							> | ||||
| 							<a | ||||
| 								href="#beilage-1-page-{{ $page }}" | ||||
| 								class="page-number-inhalts font-bold text-slate-700 bg-amber-50 px-2 py-1 rounded text-sm transition-colors duration-200 hover:bg-amber-100 no-underline" | ||||
| @@ -147,50 +184,68 @@ | ||||
| 					<div class="space-y-0"> | ||||
| 						{{ if $pageItems }} | ||||
| 							{{ range $individualPiece := $pageItems }} | ||||
| 							<div | ||||
| 								class="inhalts-entry py-1 px-0 bg-slate-50 rounded hover:bg-slate-100 transition-colors duration-200 {{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 									continuation-entry | ||||
| 								{{ end }}" | ||||
| 								data-page="{{ $page }}" | ||||
| 								{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 									data-is-continuation="true" | ||||
| 								{{ end }} | ||||
| 								style="{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 									display: none; | ||||
| 								{{ end }}"> | ||||
| 								{{ template "_inhaltsverzeichnis_eintrag" $individualPiece.PieceByIssue }} | ||||
| 								<div | ||||
| 									class="inhalts-entry py-1 px-0 bg-slate-50 rounded hover:bg-slate-100 transition-colors duration-200 {{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 										continuation-entry | ||||
| 									{{ end }}" | ||||
| 									data-page="{{ $page }}" | ||||
| 									{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 										data-is-continuation="true" | ||||
| 									{{ end }} | ||||
| 									style="{{ if $individualPiece.PieceByIssue.IsContinuation }} | ||||
| 										display: none; | ||||
| 									{{ end }}"> | ||||
| 									{{ template "_inhaltsverzeichnis_eintrag" $individualPiece.PieceByIssue }} | ||||
|  | ||||
|  | ||||
| 								<!-- Links zu anderen Teilen: --> | ||||
| 								{{ if and (not $individualPiece.PieceByIssue.IsContinuation) (gt (len $individualPiece.IssueRefs) 1) }} | ||||
| 									<div class="mt-1 pt-1 border-t border-slate-100"> | ||||
| 										<div class="flex items-center flex-wrap gap-1"> | ||||
| 											<i class="ri-links-line text-slate-600 text-sm mr-1"></i> | ||||
| 											{{ range $issue := $individualPiece.IssueRefs }} | ||||
| 												<a | ||||
| 													href="/{{- $issue.When -}}/{{- $issue.Nr -}}{{- if $issue.Von -}} | ||||
| 														{{- if $issue.Beilage -}} | ||||
| 															#beilage-{{ $issue.Beilage }}-page-{{ $issue.Von }} | ||||
| 														{{- else -}} | ||||
| 															#page-{{ $issue.Von }} | ||||
| 														{{- end -}} | ||||
| 													{{- end -}}" | ||||
| 													class="inline-flex items-center gap-1 px-2 py-1 bg-slate-100 text-slate-700 rounded-md text-xs font-medium hover:bg-slate-200 transition-colors duration-150" | ||||
| 													{{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year | ||||
| 														$model.Datum.When.Year) | ||||
| 													-}} | ||||
| 														aria-current="page" | ||||
| 													{{ end }}> | ||||
| 													<i class="ri-calendar-line text-xs"></i> | ||||
| 													{{- $issue.When.Year }} Nr. | ||||
| 													{{ $issue.Nr -}} | ||||
| 												</a> | ||||
| 											{{ end }} | ||||
| 									<!-- Links zu anderen Teilen: --> | ||||
| 									{{ if and (not $individualPiece.PieceByIssue.IsContinuation) (gt (len $individualPiece.IssueRefs) 1) }} | ||||
| 										<div class="mt-1 pt-1 border-t border-slate-100"> | ||||
| 											<div class="flex items-center flex-wrap gap-1"> | ||||
| 												<i class="ri-links-line text-slate-600 text-sm mr-1"></i> | ||||
| 												{{ range $index, $issue := $individualPiece.IssueRefs }} | ||||
| 													<div | ||||
| 														class="inline-flex items-center gap-1 px-2 py-1 bg-slate-100 rounded-md text-xs font-medium hover:bg-slate-200 transition-colors duration-150 {{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year $model.Datum.When.Year) -}} | ||||
| 															bg-red-100 text-red-700 | ||||
| 														{{ end }}"> | ||||
| 														<span class="text-black text-xs font-bold">{{ add $index 1 }}</span> | ||||
| 														<span class="w-px h-3 bg-slate-300 mx-1"></span> | ||||
| 														<a | ||||
| 															href="/{{- $issue.When -}}/{{- $issue.Nr -}}{{- if $issue.Von -}} | ||||
| 																{{- if $issue.Beilage -}} | ||||
| 																	#beilage-{{ $issue.Beilage }}-page-{{ $issue.Von }} | ||||
| 																{{- else -}} | ||||
| 																	#page-{{ $issue.Von }} | ||||
| 																{{- end -}} | ||||
| 															{{- end -}}" | ||||
| 															class="text-slate-700 no-underline hover:text-slate-900 {{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year $model.Datum.When.Year) -}} | ||||
| 																text-red-700 pointer-events-none | ||||
| 															{{ end }}" | ||||
| 															{{- if and (eq $issue.Nr $model.Number.No) (eq $issue.When.Year | ||||
| 																$model.Datum.When.Year) | ||||
| 															-}} | ||||
| 																aria-current="page" | ||||
| 															{{ end }}> | ||||
| 															{{- $issueKey := printf "%d-%d" $issue.When.Year $issue.Nr -}} | ||||
| 															{{- $issueData := GetIssue $issueKey -}} | ||||
| 															{{- if $issueData -}} | ||||
| 																{{ $issueData.Datum.When.Day }}.{{ $issueData.Datum.When.Month }}.{{ $issueData.Datum.When.Year }} | ||||
| 															{{- else -}} | ||||
| 																{{ $issue.When.Year }} Nr. | ||||
| 																{{ $issue.Nr }} | ||||
| 															{{- end -}} | ||||
| 															{{- if $issue.Von }} | ||||
| 																S. | ||||
| 																{{ $issue.Von }} | ||||
| 															{{- end -}} | ||||
| 														</a> | ||||
| 													</div> | ||||
| 												{{ end }} | ||||
| 											</div> | ||||
| 										</div> | ||||
| 									</div> | ||||
| 								{{ end }} | ||||
| 							</div> | ||||
| 						{{ end }} | ||||
| 									{{ end }} | ||||
| 								</div> | ||||
| 							{{ end }} | ||||
| 						{{ else }} | ||||
| 							<!-- Empty page indicator --> | ||||
| 							<div class="inhalts-entry py-1 px-0"> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Reference in New Issue
	
	Block a user
	 Simon Martens
					Simon Martens