–>
/**
* 信纸模态窗口管理
* 实现点击信纸打开模态窗口,并提供导航功能
*/
document.addEventListener(“DOMContentLoaded”, function() {
const pageLoader = document.getElementById(‘page-loader’);
if (pageLoader) pageLoader.style.display = ‘block’;
try {
// 初始化信纸元素 (MODIFIED)
initLetterPapers();
// 创建和初始化模态窗口
createModalIfNotExists(); // Renamed for clarity
// 模态窗口功能初始化
initModalFunctionality();
// 添加无障碍支持
addAccessibilitySupport();
} catch (error) {
console.error(‘初始化失败:’, error);
} finally {
// 隐藏加载指示器
if (pageLoader) pageLoader.style.display = ‘none’;
}
});
/**
* Helper function to build and append a single letter paper.
* @param {Array} elements – Array of HTML elements for this entry.
* @param {HTMLElement} container – The parent container to append the letter paper to.
*/
function buildAndAppendLetterPaper(elements, container) {
if (!elements || elements.length === 0) return;
const letterPaperDiv = document.createElement(‘div’);
// This class is crucial for styling and for the modal script to find these elements
letterPaperDiv.classList.add(‘letter-paper’);
// The original script targeted .wp-block-group.letter-paper.
// We can add .wp-block-group if needed for stricter CSS, but .letter-paper should be enough.
// letterPaperDiv.classList.add(‘wp-block-group’);
const innerContainer = document.createElement(‘div’);
// Mimic the original structure that showModalContent expects for content extraction
innerContainer.classList.add(‘wp-block-group__inner-container’, ‘is-layout-constrained’, ‘wp-block-group-is-layout-constrained’);
elements.forEach(el => {
innerContainer.appendChild(el); // el is already a clone from the calling function
});
letterPaperDiv.appendChild(innerContainer);
// Add accessibility attributes
letterPaperDiv.setAttribute(‘tabindex’, ‘0’);
letterPaperDiv.setAttribute(‘role’, ‘button’);
const h4Title = innerContainer.querySelector(‘h4.wp-block-heading’); // Ensure we get the correct H4
if (h4Title) {
letterPaperDiv.setAttribute(‘aria-label’, `打开日记: ${h4Title.textContent.trim()}`);
} else {
letterPaperDiv.setAttribute(‘aria-label’, ‘打开日记条目’);
}
container.appendChild(letterPaperDiv);
}
/**
* 初始化信纸元素 (MODIFIED for new log format)
* Parses flat WordPress block structure into letter paper cards.
*/
function initLetterPapers() {
const logContainerSelector = ‘.entry-content’; // Common WordPress content area
const mainLogContainer = document.querySelector(logContainerSelector);
if (!mainLogContainer) {
console.warn(`Log container “${logContainerSelector}” not found. Script will not process logs.`);
// Try to find if the elements are direct children of body for testing purposes
// This part is for robustness if .entry-content is missing in a test setup.
// In a real WP environment, .entry-content or a theme equivalent should exist.
const testNodes = Array.from(document.body.childNodes).filter(n => n.nodeType === 1 && n.classList.contains(‘wp-block-heading’));
if (testNodes.length > 0 && !document.querySelector(‘.entry-content’)) {
console.warn(“No ‘.entry-content’ found, but found WP blocks in body. Attempting to process body children. This is not recommended for production.”);
// In this specific test case, let’s wrap body contents for processing
const tempWrapper = document.createElement(‘div’);
tempWrapper.classList.add(‘entry-content’); // Give it the class we expect
while(document.body.firstChild && document.body.firstChild.id !== ‘page-loader’ && !document.body.firstChild.classList.contains(‘modal-overlay’)) {
tempWrapper.appendChild(document.body.firstChild);
}
document.body.insertBefore(tempWrapper, document.getElementById(‘page-loader’));
// Re-assign mainLogContainer
// mainLogContainer = tempWrapper; // This line caused an issue, it should be used if we process from body.
// For the provided HTML, .entry-content exists.
} else if (!mainLogContainer) {
return;
}
}
// If mainLogContainer is still null after the warning and potential body processing attempt.
if (!mainLogContainer) {
console.error(`Log container “${logContainerSelector}” definitively not found. Aborting initLetterPapers.`);
return;
}
const originalNodes = Array.from(mainLogContainer.childNodes);
const processedContentContainer = document.createElement(‘div’);
processedContentContainer.className = ‘clearfix’; // To contain floated letter-papers
let currentEntryElements = [];
let collectingEntry = false;
originalNodes.forEach(node => {
// We are interested in element nodes (type 1) and relevant WP comment nodes (type 8)
if (node.nodeType !== 1 && node.nodeType !== 8) {
// If it’s a text node with only whitespace, ignore. Otherwise, preserve non-block text.
if (node.nodeType === 3 && node.textContent.trim() === ”) return;
if (!collectingEntry) { // Preserve elements before the first H4 entry starts
processedContentContainer.appendChild(node.cloneNode(true));
} else { // If inside an entry, and it’s not a block comment
if(node.nodeType !== 8) currentEntryElements.push(node.cloneNode(true));
}
return;
}
// Handle WordPress comment nodes – the actual element is usually the next ELEMENT sibling
let elementNode = node;
if (node.nodeType === 8) {
let nextSibling = node.nextSibling;
while(nextSibling && nextSibling.nodeType !== 1) {
nextSibling = nextSibling.nextSibling;
}
if (nextSibling) {
elementNode = nextSibling;
} else {
// This comment node doesn’t have an associated element, or it’s a closing block comment.
// If it’s a closing block like /wp:shortcode and we are not collecting, add the comment itself.
if (!collectingEntry && node.nodeValue.trim().startsWith(‘/wp:’)) {
processedContentContainer.appendChild(node.cloneNode(true));
}
return;
}
}
// Now elementNode is an actual HTML element
if (elementNode.tagName === ‘H4’ && elementNode.classList.contains(‘wp-block-heading’)) {
if (collectingEntry && currentEntryElements.length > 0) {
buildAndAppendLetterPaper(currentEntryElements, processedContentContainer);
}
currentEntryElements = [elementNode.cloneNode(true)];
collectingEntry = true;
} else if (collectingEntry) {
// Skip top-level separators that are truly between entries.
// A separator is inter-entry if the *next actual block* is an H4.
if (elementNode.tagName === ‘HR’ && elementNode.classList.contains(‘wp-block-separator’)) {
let nextActualBlock = elementNode.nextElementSibling;
while(nextActualBlock && nextActualBlock.nodeType === 8) { // Skip over comment nodes
let temp = nextActualBlock.nextElementSibling;
if (temp && temp.nodeType === 1) nextActualBlock = temp;
else break;
}
if (nextActualBlock && nextActualBlock.tagName === ‘H4’ && nextActualBlock.classList.contains(‘wp-block-heading’)) {
// This separator is between entries, so skip it.
} else {
currentEntryElements.push(elementNode.cloneNode(true));
}
} else {
currentEntryElements.push(elementNode.cloneNode(true));
}
} else {
// Element before the first H4 (e.g., music player)
// The WP comment for the shortcode is preserved, then the shortcode div itself.
if(node.nodeType === 8) processedContentContainer.appendChild(node.cloneNode(true)); // Add the wp:shortcode comment
processedContentContainer.appendChild(elementNode.cloneNode(true)); // Add the actual div.wp-block-shortcode
if(node.nodeType === 8) { // Add the /wp:shortcode comment if it’s paired
let closingComment = elementNode.nextSibling;
while(closingComment && closingComment.nodeType !== 8) closingComment = closingComment.nextSibling;
if (closingComment && closingComment.nodeValue.trim().startsWith(‘/wp:’)) {
processedContentContainer.appendChild(closingComment.cloneNode(true));
}
}
}
});
// Append the last collected entry
if (collectingEntry && currentEntryElements.length > 0) {
buildAndAppendLetterPaper(currentEntryElements, processedContentContainer);
}
// Replace the original content of mainLogContainer with the new structured content
mainLogContainer.innerHTML = ”;
mainLogContainer.appendChild(processedContentContainer);
}
/**
* Creates modal elements if they don’t already exist in the DOM.
*/
function createModalIfNotExists() {
if (document.querySelector(‘.modal-overlay’)) {
return; // Modal already exists
}
const modalOverlay = document.createElement(‘div’);
modalOverlay.classList.add(‘modal-overlay’);
modalOverlay.setAttribute(‘role’, ‘dialog’);
modalOverlay.setAttribute(‘aria-modal’, ‘true’);
modalOverlay.setAttribute(‘aria-labelledby’, ‘modal-title’); // Title will be set dynamically
const modalContentWrapper = document.createElement(‘div’);
modalContentWrapper.classList.add(‘modal-content’); // This is the styled “paper” for the modal
const modalInnerContent = document.createElement(‘div’);
modalInnerContent.setAttribute(‘id’, ‘modal-content-inner’); // Where actual content goes
modalContentWrapper.appendChild(modalInnerContent);
const loader = document.createElement(‘div’);
loader.classList.add(‘loader’);
loader.setAttribute(‘id’, ‘modal-loader’);
modalContentWrapper.appendChild(loader); // Loader inside content wrapper for centering
const closeButton = document.createElement(‘button’);
closeButton.innerHTML = ‘×’;
closeButton.classList.add(‘close-button’);
closeButton.setAttribute(‘aria-label’, ‘关闭’);
closeButton.setAttribute(‘title’, ‘关闭’);
modalContentWrapper.appendChild(closeButton); // Close button also part of the paper
// Nav buttons are part of the overlay, not the content paper
const prevButton = document.createElement(‘button’);
prevButton.classList.add(‘nav-button’, ‘prev-button’);
prevButton.setAttribute(‘aria-label’, ‘上一个’);
prevButton.setAttribute(‘title’, ‘上一个’);
modalOverlay.appendChild(prevButton);
const nextButton = document.createElement(‘button’);
nextButton.classList.add(‘nav-button’, ‘next-button’);
nextButton.setAttribute(‘aria-label’, ‘下一个’);
nextButton.setAttribute(‘title’, ‘下一个’);
modalOverlay.appendChild(nextButton);
modalOverlay.appendChild(modalContentWrapper);
document.body.appendChild(modalOverlay);
}
/**
* (Original createModal function renamed and adapted slightly)
* Create modal elements if they don’t already exist.
*/
function createModal() { // This is the original function, now createModalIfNotExists is preferred
createModalIfNotExists();
}
/**
* 初始化模态窗口功能
*/
function initModalFunctionality() {
// IMPORTANT: Query for .letter-paper AFTER initLetterPapers has run and created them.
const letterPapers = document.querySelectorAll(‘.letter-paper’);
const body = document.body;
const modalOverlay = document.querySelector(‘.modal-overlay’);
const modalContent = document.querySelector(‘#modal-content-inner’);
const closeButton = document.querySelector(‘.close-button’);
const prevButton = document.querySelector(‘.prev-button’);
const nextButton = document.querySelector(‘.next-button’);
const modalLoader = document.querySelector(‘#modal-loader’);
if (!modalOverlay || !modalContent || !closeButton || !prevButton || !nextButton || !modalLoader) {
console.error(“Modal elements not found. Cannot initialize modal functionality.”);
return;
}
let currentIndex = -1;
function closeModal() {
modalOverlay.classList.remove(‘active’);
setTimeout(() => {
modalOverlay.style.display = ‘none’;
modalContent.innerHTML = ”;
body.classList.remove(‘no-scroll’);
}, 300); // Match transition duration
}
function showModalContent(letterPaper) {
modalLoader.style.display = ‘block’;
modalContent.innerHTML = ”; // Clear previous content
setTimeout(() => {
try {
// The content for the modal is directly within the .wp-block-group__inner-container
const contentContainer = letterPaper.querySelector(‘.wp-block-group__inner-container’);
if (contentContainer) {
const safeContent = contentContainer.cloneNode(true);
const links = safeContent.querySelectorAll(‘a’);
links.forEach(link => {
link.setAttribute(‘target’, ‘_blank’);
link.setAttribute(‘rel’, ‘noopener noreferrer’);
});
modalContent.appendChild(safeContent);
const titleElement = safeContent.querySelector(‘h4.wp-block-heading’);
if (titleElement) {
modalOverlay.setAttribute(‘aria-labelledby’, ‘modal-title-dynamic’); // Give an ID to the h4
titleElement.id = ‘modal-title-dynamic’;
} else {
modalOverlay.removeAttribute(‘aria-labelledby’); // No specific title
}
modalOverlay.style.display = ‘flex’;
setTimeout(() => modalOverlay.classList.add(‘active’), 10); // For transition
body.classList.add(‘no-scroll’);
updateNavigationButtons();
} else {
throw new Error(“Content container not found in letter paper.”);
}
} catch (error) {
console.error(‘加载内容失败:’, error);
showError(‘内容加载失败,请稍后重试.’);
} finally {
modalLoader.style.display = ‘none’;
}
}, 200);
}
function showError(message) {
modalContent.innerHTML = `
出错了
${message}
`;
modalOverlay.setAttribute(‘aria-labelledby’, ‘modal-title-dynamic’);
}
function updateNavigationButtons() {
prevButton.style.visibility = (currentIndex > 0) ? ‘visible’ : ‘hidden’;
nextButton.style.visibility = (currentIndex 0) {
currentIndex–;
showModalContent(letterPapers[currentIndex]);
// Ensure the new active element is focused for screen readers
letterPapers[currentIndex].focus();
}
}
function navigateToNext() {
if (currentIndex { event.stopPropagation(); navigateToPrevious(); });
nextButton.addEventListener(‘click’, (event) => { event.stopPropagation(); navigateToNext(); });
letterPapers.forEach((letterPaper, index) => {
letterPaper.addEventListener(‘click’, (event) => {
currentIndex = index;
showModalContent(letterPaper);
event.stopPropagation();
});
letterPaper.addEventListener(‘keydown’, (event) => {
if (event.key === ‘Enter’ || event.key === ‘ ‘) {
currentIndex = index;
showModalContent(letterPaper);
event.preventDefault();
}
});
});
modalOverlay.addEventListener(‘click’, (event) => {
if (event.target === modalOverlay) {
closeModal();
}
});
modalOverlay.style.display = ‘none’; // Ensure it’s hidden on load
}
/**
* 添加键盘导航和无障碍支持
*/
function addAccessibilitySupport() {
document.addEventListener(‘keydown’, (event) => {
const modalOverlay = document.querySelector(‘.modal-overlay’);
if (modalOverlay && modalOverlay.style.display === ‘flex’ && modalOverlay.classList.contains(‘active’)) {
switch(event.key) {
case ‘Escape’:
document.querySelector(‘.close-button’).click();
break;
case ‘ArrowLeft’:
const prevButton = document.querySelector(‘.prev-button’);
if (prevButton.style.visibility !== ‘hidden’) prevButton.click();
break;
case ‘ArrowRight’:
const nextButton = document.querySelector(‘.next-button’);
if (nextButton.style.visibility !== ‘hidden’) nextButton.click();
break;
}
}
});
const modalOverlayForFocusTrap = document.querySelector(‘.modal-overlay’);
if (modalOverlayForFocusTrap) {
modalOverlayForFocusTrap.addEventListener(‘keydown’, function(event) {
if (event.key === ‘Tab’) {
const focusableElements = this.querySelectorAll(‘button, [href], input, select, textarea, [tabindex]:not([tabindex=”-1″])’);
const visibleFocusableElements = Array.from(focusableElements).filter(el => {
return el.offsetParent !== null && window.getComputedStyle(el).visibility !== ‘hidden’;
});
if (visibleFocusableElements.length === 0) {
event.preventDefault();
return;
}
const firstElement = visibleFocusableElements[0];
const lastElement = visibleFocusableElements[visibleFocusableElements.length – 1];
if (event.shiftKey) {
if (document.activeElement === firstElement) {
lastElement.focus();
event.preventDefault();
}
} else {
if (document.activeElement === lastElement) {
firstElement.focus();
event.preventDefault();
}
}
}
});
}
}
/**
* 添加懒加载支持 (Optional, original function)
* 当信纸元素进入视口时才加载内容 (currently placeholder images)
*/
function initLazyLoading() {
if (‘IntersectionObserver’ in window) {
const letterPapers = document.querySelectorAll(‘.letter-paper’); // Target dynamically created papers
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const letterPaper = entry.target;
// Example: Load images within the letter paper if they use data-src
const imagesToLoad = letterPaper.querySelectorAll(‘img[data-src]’);
imagesToLoad.forEach(img => {
img.src = img.getAttribute(‘data-src’);
img.removeAttribute(‘data-src’);
});
letterPaper.style.animationDelay = (Math.random() * 0.5) + ‘s’; // If you have entry animations
observer.unobserve(letterPaper);
}
});
}, {
root: null,
rootMargin: ’50px’, /* Preload a bit before visible */
threshold: 0.1 /* 10% visible */
});
letterPapers.forEach(paper => observer.observe(paper));
}
}
// Initialize lazy loading after other DOM manipulations are complete
window.addEventListener(‘load’, initLazyLoading);
本来今天训练回来没想继续做实验,于是九点多就躺床上刷手机准备睡觉了,结果老板在群里丢了一句 “Looking forward to the screen result.”我才知道我做的CRISPR screen(特别费钱 我做了好几个月的大实验)测序结果出了,因为我没电脑,所以用手机登录邮箱看了看附件里的质控报告。