mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-29 00:55:32 +00:00
595 lines
18 KiB
JavaScript
595 lines
18 KiB
JavaScript
// ===========================
|
|
// ISSUE/NEWSPAPER LAYOUT FUNCTIONS
|
|
// ===========================
|
|
|
|
// Global variables for state management
|
|
window.currentPageContainers = window.currentPageContainers || [];
|
|
window.currentActiveIndex = window.currentActiveIndex || 0;
|
|
window.pageObserver = window.pageObserver || null;
|
|
|
|
// Modal functions
|
|
export function enlargePage(imgElement, pageNumber, isFromSpread, partNumber = null) {
|
|
// Get or create the single page viewer component
|
|
let viewer = document.querySelector("single-page-viewer");
|
|
if (!viewer) {
|
|
viewer = document.createElement("single-page-viewer");
|
|
document.body.appendChild(viewer);
|
|
}
|
|
|
|
// Determine if this is a beilage page
|
|
const isBeilage = imgElement.closest('[data-beilage="true"]') !== null;
|
|
|
|
// Get target page from template data if available
|
|
const targetPage =
|
|
window.templateData && window.templateData.targetPage ? window.templateData.targetPage : 0;
|
|
|
|
// Show the page in the viewer
|
|
viewer.show(imgElement.src, imgElement.alt, pageNumber, isBeilage, targetPage, partNumber);
|
|
}
|
|
|
|
export function closeModal() {
|
|
const modal = document.getElementById("pageModal");
|
|
modal.classList.add("hidden");
|
|
}
|
|
|
|
// Page navigation functions
|
|
export 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);
|
|
});
|
|
}
|
|
}
|
|
|
|
export function scrollToPreviousPage() {
|
|
if (window.currentActiveIndex > 0) {
|
|
// Find the first page that's not currently visible
|
|
let targetIndex = -1;
|
|
|
|
// Check which pages are currently visible
|
|
const visibleIndexes = [];
|
|
window.currentPageContainers.forEach((container, index) => {
|
|
const containerRect = container.getBoundingClientRect();
|
|
const viewportHeight = window.innerHeight;
|
|
|
|
const visibleTop = Math.max(containerRect.top, 0);
|
|
const visibleBottom = Math.min(containerRect.bottom, viewportHeight);
|
|
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
const containerHeight = containerRect.height;
|
|
|
|
const visibilityRatio = visibleHeight / containerHeight;
|
|
if (visibilityRatio >= 0.3) {
|
|
// Consider visible if 30% or more is showing
|
|
visibleIndexes.push(index);
|
|
}
|
|
});
|
|
|
|
// Find the first non-visible page before the current visible range
|
|
const minVisibleIndex = Math.min(...visibleIndexes);
|
|
for (let i = minVisibleIndex - 1; i >= 0; i--) {
|
|
if (!visibleIndexes.includes(i)) {
|
|
targetIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no non-visible page found, go to the page just before the visible range
|
|
if (targetIndex === -1 && minVisibleIndex > 0) {
|
|
targetIndex = minVisibleIndex - 1;
|
|
}
|
|
|
|
if (targetIndex >= 0) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function scrollToNextPage() {
|
|
if (window.currentActiveIndex < window.currentPageContainers.length - 1) {
|
|
// Find the first page that's not currently visible
|
|
let targetIndex = -1;
|
|
|
|
// Check which pages are currently visible
|
|
const visibleIndexes = [];
|
|
window.currentPageContainers.forEach((container, index) => {
|
|
const containerRect = container.getBoundingClientRect();
|
|
const viewportHeight = window.innerHeight;
|
|
|
|
const visibleTop = Math.max(containerRect.top, 0);
|
|
const visibleBottom = Math.min(containerRect.bottom, viewportHeight);
|
|
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
const containerHeight = containerRect.height;
|
|
|
|
const visibilityRatio = visibleHeight / containerHeight;
|
|
if (visibilityRatio >= 0.3) {
|
|
// Consider visible if 30% or more is showing
|
|
visibleIndexes.push(index);
|
|
}
|
|
});
|
|
|
|
// Find the first non-visible page after the current visible range
|
|
const maxVisibleIndex = Math.max(...visibleIndexes);
|
|
for (let i = maxVisibleIndex + 1; i < window.currentPageContainers.length; i++) {
|
|
if (!visibleIndexes.includes(i)) {
|
|
targetIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If no non-visible page found, go to the page just after the visible range
|
|
if (targetIndex === -1 && maxVisibleIndex < window.currentPageContainers.length - 1) {
|
|
targetIndex = maxVisibleIndex + 1;
|
|
}
|
|
|
|
if (targetIndex >= 0 && targetIndex < window.currentPageContainers.length) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function scrollToBeilage() {
|
|
// Check if we're currently viewing a Beilage section
|
|
const isViewingBeilage = isCurrentlyInBeilageSection();
|
|
|
|
if (isViewingBeilage) {
|
|
// Go back to main issue (first main page)
|
|
const firstMainPage = document.querySelector("#newspaper-content .newspaper-page-container");
|
|
if (firstMainPage) {
|
|
firstMainPage.scrollIntoView({
|
|
behavior: "smooth",
|
|
block: "start",
|
|
});
|
|
}
|
|
} else {
|
|
// Go to first beilage container
|
|
const beilageContainer = document.querySelector('[class*="border-t-2 border-amber-200"]');
|
|
if (beilageContainer) {
|
|
beilageContainer.scrollIntoView({
|
|
behavior: "smooth",
|
|
block: "start",
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function isCurrentlyInBeilageSection() {
|
|
// Check which pages are currently visible
|
|
const visibleIndexes = [];
|
|
window.currentPageContainers.forEach((container, index) => {
|
|
const containerRect = container.getBoundingClientRect();
|
|
const viewportHeight = window.innerHeight;
|
|
|
|
const visibleTop = Math.max(containerRect.top, 0);
|
|
const visibleBottom = Math.min(containerRect.bottom, viewportHeight);
|
|
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
const containerHeight = containerRect.height;
|
|
|
|
const visibilityRatio = visibleHeight / containerHeight;
|
|
if (visibilityRatio >= 0.3) {
|
|
// Consider visible if 30% or more is showing
|
|
visibleIndexes.push(index);
|
|
}
|
|
});
|
|
|
|
// Check if any visible page is a Beilage page
|
|
for (const index of visibleIndexes) {
|
|
const container = window.currentPageContainers[index];
|
|
if (container && container.id && container.id.includes("beilage-")) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
export function updateButtonStates() {
|
|
const prevBtn = document.getElementById("prevPageBtn");
|
|
const nextBtn = document.getElementById("nextPageBtn");
|
|
const beilageBtn = document.getElementById("beilageBtn");
|
|
|
|
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";
|
|
}
|
|
}
|
|
|
|
// Update Beilage button based on current location
|
|
if (beilageBtn) {
|
|
const isViewingBeilage = isCurrentlyInBeilageSection();
|
|
const icon = beilageBtn.querySelector("i");
|
|
|
|
if (isViewingBeilage) {
|
|
// Show "Go to Main Issue" state - use gray styling
|
|
beilageBtn.title = "Zur Hauptausgabe";
|
|
beilageBtn.className =
|
|
"w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-800 border border-gray-300 transition-colors duration-200 flex items-center justify-center cursor-pointer";
|
|
if (icon) {
|
|
icon.className = "ri-file-text-line text-lg lg:text-xl";
|
|
}
|
|
} else {
|
|
// Show "Go to Beilage" state - use amber styling
|
|
beilageBtn.title = "Zu Beilage";
|
|
beilageBtn.className =
|
|
"w-14 h-10 lg:w-16 lg:h-12 px-2 py-1 bg-amber-100 hover:bg-amber-200 text-amber-700 hover:text-amber-800 border border-amber-300 transition-colors duration-200 flex items-center justify-center cursor-pointer";
|
|
if (icon) {
|
|
icon.className = "ri-attachment-line text-lg lg:text-xl";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Share and citation functions
|
|
export 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 = `/${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);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function generateCitation() {
|
|
const button = document.getElementById("citationBtn");
|
|
|
|
// Get current page and issue information
|
|
const issueInfo = document.title || "KGPZ";
|
|
|
|
// Use clean URL without hash fragments
|
|
let cleanUrl = window.location.origin + window.location.pathname;
|
|
|
|
// Remove any hash fragments that might still exist
|
|
if (cleanUrl.includes("#")) {
|
|
cleanUrl = cleanUrl.split("#")[0];
|
|
}
|
|
|
|
// 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: ${cleanUrl} (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 viewportHeight = window.innerHeight;
|
|
const viewportWidth = window.innerWidth;
|
|
|
|
// Calculate position relative to viewport (since we're using fixed positioning)
|
|
let left = buttonRect.left - 10;
|
|
let top = buttonRect.bottom + 8;
|
|
|
|
// Ensure popup doesn't go off-screen
|
|
const popupWidth = 120; // Estimated popup width
|
|
const popupHeight = 32; // Estimated popup height
|
|
|
|
// Adjust horizontal position if too far right
|
|
if (left + popupWidth > viewportWidth) {
|
|
left = buttonRect.right - popupWidth + 10;
|
|
}
|
|
|
|
// Adjust vertical position if too far down (show above button instead)
|
|
if (top + popupHeight > viewportHeight) {
|
|
top = buttonRect.top - popupHeight - 8;
|
|
}
|
|
|
|
popup.style.left = Math.max(5, left) + "px";
|
|
popup.style.top = Math.max(5, top) + "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);
|
|
}
|
|
|
|
// Page-specific utilities
|
|
export function copyPagePermalink(pageNumber, button, isBeilage = false) {
|
|
let pageUrl = "";
|
|
if (isBeilage) {
|
|
// For beilage pages, still use hash for now until beilage URLs are updated
|
|
pageUrl = window.location.origin + window.location.pathname + `#beilage-1-page-${pageNumber}`;
|
|
} else {
|
|
// For regular pages, use the new URL format
|
|
const pathParts = window.location.pathname.split("/");
|
|
if (pathParts.length >= 3) {
|
|
// Current URL format: /year/issue or /year/issue/page
|
|
// New format: /year/issue/page
|
|
const year = pathParts[1];
|
|
const issue = pathParts[2];
|
|
pageUrl = `${window.location.origin}/${year}/${issue}/${pageNumber}`;
|
|
} else {
|
|
// Fallback to current URL with page appended
|
|
pageUrl = window.location.origin + window.location.pathname + `/${pageNumber}`;
|
|
}
|
|
}
|
|
|
|
const currentUrl = pageUrl;
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function generatePageCitation(pageNumber, button) {
|
|
// Get current issue information
|
|
const issueInfo = document.title || "KGPZ";
|
|
|
|
// Generate page URL in new format
|
|
const pathParts = window.location.pathname.split("/");
|
|
let pageUrl;
|
|
if (pathParts.length >= 3) {
|
|
const year = pathParts[1];
|
|
const issue = pathParts[2];
|
|
pageUrl = `${window.location.origin}/${year}/${issue}/${pageNumber}`;
|
|
} else {
|
|
pageUrl = `${window.location.origin}${window.location.pathname}/${pageNumber}`;
|
|
}
|
|
|
|
const currentUrl = pageUrl;
|
|
|
|
// Basic citation format for specific page
|
|
const currentDate = new Date().toLocaleDateString("de-DE");
|
|
const citation = `Königsberger Gelehrte und Politische Zeitung (KGPZ). ${issueInfo}, Seite ${pageNumber}. 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize newspaper layout functionality
|
|
export function initializeNewspaperLayout() {
|
|
// Initialize page tracking
|
|
initializePageTracking();
|
|
|
|
// Set up scroll handler
|
|
window.addEventListener("scroll", function () {
|
|
clearTimeout(window.scrollTimeout);
|
|
window.scrollTimeout = setTimeout(() => {
|
|
updateButtonStates(); // Update button states including Beilage toggle
|
|
}, 50);
|
|
});
|
|
|
|
// Set up keyboard shortcuts
|
|
document.addEventListener("keydown", function (e) {
|
|
if (e.key === "Escape") {
|
|
closeModal();
|
|
}
|
|
});
|
|
} |