mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-30 01:25:30 +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