mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-29 17:15:31 +00:00
THE GREAT JS CLEANUP
This commit is contained in:
595
views/transform/issue.js
Normal file
595
views/transform/issue.js
Normal file
@@ -0,0 +1,595 @@
|
||||
// ===========================
|
||||
// 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user