add _extensions

This commit is contained in:
2026-05-21 13:37:53 +08:00
parent 6a9a5fc90e
commit 61bd0bea2f
252 changed files with 33972 additions and 1 deletions
@@ -0,0 +1,418 @@
// clab-tooltip.js - 完全隔离事件版本(修改后的显示位置逻辑)
(function() {
// 全局变量存储从 RN.html 提取的内容
let rnContentCache = {};
let isRnContentLoaded = false;
let activeTooltip = null;
let hideTimer = null;
let isMouseOverTooltip = false;
// 从 RN.html 提取内容(保持不变)
async function loadRnContent() {
try {
console.log('加载 RN.html...');
const response = await fetch('RN.html');
if (!response.ok) throw new Error(`HTTP error: ${response.status}`);
const htmlText = await response.text();
const parser = new DOMParser();
const rnDoc = parser.parseFromString(htmlText, 'text/html');
// 查找所有包含 data-options 的元素
const optionElements = rnDoc.querySelectorAll('[data-options]');
console.log(`找到 ${optionElements.length} 个 data-options 元素`);
optionElements.forEach(element => {
const optionValue = element.getAttribute('data-options');
if (!optionValue) return;
// 向前查找最近的 blockquote
let prevElement = element.previousElementSibling;
let blockquote = null;
// 先找前一个兄弟元素
while (prevElement) {
if (prevElement.tagName === 'BLOCKQUOTE') {
blockquote = prevElement;
break;
}
prevElement = prevElement.previousElementSibling;
}
// 如果没找到,向上查找
if (!blockquote) {
let parent = element.parentElement;
while (parent) {
let sibling = parent.previousElementSibling;
while (sibling) {
if (sibling.tagName === 'BLOCKQUOTE') {
blockquote = sibling;
break;
}
sibling = sibling.previousElementSibling;
}
if (blockquote) break;
parent = parent.parentElement;
}
}
if (blockquote) {
const lis = blockquote.querySelectorAll('li');
if (lis.length > 0) {
const liContents = Array.from(lis).map(li => li.textContent.trim());
if (!rnContentCache[optionValue]) {
rnContentCache[optionValue] = [];
}
// 存储所有引用
rnContentCache[optionValue].push({
contents: liContents
});
}
}
});
isRnContentLoaded = true;
console.log('内容加载完成');
Object.keys(rnContentCache).forEach(key => {
console.log(`"${key}": ${rnContentCache[key].length} Questions`);
});
} catch (error) {
console.error('加载失败:', error);
}
}
// 创建 tooltip(保持不变)
function createTooltip() {
const tooltip = document.createElement('div');
tooltip.className = 'clab-tooltip';
tooltip.id = 'clab-tooltip-element';
// 完全阻止所有事件冒泡
const stopAllEvents = (e) => {
e.stopPropagation();
e.stopImmediatePropagation();
e.preventDefault();
return false;
};
// 阻止所有可能的事件
const events = [
'mousedown', 'mouseup', 'click', 'dblclick',
'mousemove', 'mouseover', 'mouseout', 'mouseenter', 'mouseleave',
'wheel', 'scroll',
'touchstart', 'touchmove', 'touchend', 'touchcancel',
'pointerdown', 'pointermove', 'pointerup', 'pointercancel',
'dragstart', 'dragover', 'drop'
];
events.forEach(eventType => {
tooltip.addEventListener(eventType, stopAllEvents, true); // 捕获阶段
tooltip.addEventListener(eventType, stopAllEvents, false); // 冒泡阶段
});
// 允许 wheel 事件用于滚动,但阻止冒泡
tooltip.addEventListener('wheel', function(e) {
e.stopPropagation();
e.stopImmediatePropagation();
// 允许默认的滚动行为
}, { passive: false });
// 允许鼠标在 tooltip 内移动
tooltip.addEventListener('mousemove', function(e) {
e.stopPropagation();
}, { passive: true });
document.body.appendChild(tooltip);
return tooltip;
}
// 显示 tooltip - 已修改位置逻辑
function showTooltip(element, tooltip) {
const rect = element.getBoundingClientRect();
const optionValue = element.getAttribute('data-options');
// 清空内容
tooltip.innerHTML = '';
if (rnContentCache[optionValue] && rnContentCache[optionValue].length > 0) {
const entries = rnContentCache[optionValue];
// 标题显示选项
const title = document.createElement('div');
title.className = 'tooltip-title';
title.textContent = optionValue;
if (entries.length > 1) {
title.textContent += ` (${entries.length} questions)`;
}
tooltip.appendChild(title);
// 显示所有引用
entries.forEach((entry, entryIndex) => {
const content = entry.contents;
if (content.length === 1) {
// 单一条目
const entryDiv = document.createElement('div');
entryDiv.className = 'entry-single';
if (entries.length >= 1) {
entryDiv.innerHTML = `<span class="q-label">Q${entryIndex + 1}:</span> ${content[0]}`;
} else {
entryDiv.textContent = content[0];
}
tooltip.appendChild(entryDiv);
} else {
// 多个条目
const entryDiv = document.createElement('div');
entryDiv.className = 'entry-multiple';
if (entries.length > 1) {
const header = document.createElement('div');
header.className = 'q-header';
header.textContent = `Q${entryIndex + 1}:`;
entryDiv.appendChild(header);
}
const ol = document.createElement('ol');
ol.className = 'q-list';
content.forEach((item, itemIndex) => {
const li = document.createElement('li');
li.innerHTML = `<span class="q-sublabel">${entryIndex + 1}.${itemIndex + 1}</span> ${item}`;
ol.appendChild(li);
});
entryDiv.appendChild(ol);
tooltip.appendChild(entryDiv);
}
});
} else {
// 没有找到内容
const noContent = document.createElement('div');
noContent.className = 'tooltip-no-content';
noContent.textContent = '未找到相关内容';
tooltip.appendChild(noContent);
}
// 确保 tooltip 有合适的尺寸
tooltip.style.position = 'fixed';
tooltip.style.zIndex = '10000';
tooltip.style.maxWidth = '400px';
tooltip.style.maxHeight = '500px';
tooltip.style.overflowY = 'auto';
tooltip.style.boxSizing = 'border-box';
// 首先隐藏并获取尺寸
tooltip.style.visibility = 'hidden';
tooltip.classList.add('active');
document.body.appendChild(tooltip);
const tooltipWidth = Math.min(tooltip.scrollWidth, 400);
const tooltipHeight = Math.min(tooltip.scrollHeight, 500);
tooltip.style.width = tooltipWidth + 'px';
tooltip.style.height = 'auto';
const padding = 5;
const minDistanceFromEdge = 20;
let left, top;
let position = 'right'; // 首选右侧
// 检查右侧空间
if (rect.right + padding + tooltipWidth <= window.innerWidth - minDistanceFromEdge) {
// 右侧有足够空间
left = rect.right + padding;
top = rect.top;
position = 'right';
} else if (rect.left - padding - tooltipWidth >= minDistanceFromEdge) {
// 左侧有足够空间
left = rect.left - padding - tooltipWidth;
top = rect.top;
position = 'left';
} else {
// 两侧都没有足够空间,显示在下方
left = Math.max(
minDistanceFromEdge,
Math.min(
rect.left + (rect.width - tooltipWidth) / 2,
window.innerWidth - tooltipWidth - minDistanceFromEdge
)
);
top = rect.bottom + padding;
position = 'bottom';
}
// 垂直边界检查 - 确保不超出屏幕
if (top + tooltipHeight > window.innerHeight - minDistanceFromEdge) {
// 如果下方空间不足,尝试显示在上方
if (rect.top - padding - tooltipHeight >= minDistanceFromEdge) {
top = rect.top - padding - tooltipHeight;
} else {
// 上下都不够,显示在中间
top = Math.max(
minDistanceFromEdge,
Math.min(
top,
window.innerHeight - tooltipHeight - minDistanceFromEdge
)
);
}
}
// 水平边界检查 - 确保不超出屏幕
if (position === 'right' || position === 'left') {
left = Math.max(
minDistanceFromEdge,
Math.min(left, window.innerWidth - tooltipWidth - minDistanceFromEdge)
);
}
tooltip.style.left = left + 'px';
tooltip.style.top = top + 'px';
tooltip.style.visibility = 'visible';
// 添加一个小箭头指示方向
tooltip.setAttribute('data-position', position);
activeTooltip = tooltip;
isMouseOverTooltip = true;
// 添加 overlay 防止 body 交互
addOverlay();
}
// 添加半透明 overlay(保持不变)
function addOverlay() {
// 移除现有的 overlay
removeOverlay();
const overlay = document.createElement('div');
overlay.id = 'tooltip-overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
background: transparent;
pointer-events: none;
`;
document.body.appendChild(overlay);
// 将 overlay 的 pointer-events 设为 auto,但阻止所有事件
overlay.addEventListener('mousedown', stopEvent, true);
overlay.addEventListener('mouseup', stopEvent, true);
overlay.addEventListener('click', stopEvent, true);
overlay.addEventListener('mousemove', stopEvent, true);
overlay.addEventListener('wheel', stopEvent, true);
function stopEvent(e) {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
return false;
}
}
// 移除 overlay(保持不变)
function removeOverlay() {
const overlay = document.getElementById('tooltip-overlay');
if (overlay) {
overlay.remove();
}
}
// 隐藏 tooltip(保持不变)
function hideTooltip() {
if (!isMouseOverTooltip && activeTooltip) {
activeTooltip.classList.remove('active');
activeTooltip = null;
removeOverlay();
}
}
// 强制隐藏 tooltip(保持不变)
function forceHideTooltip() {
if (activeTooltip) {
activeTooltip.classList.remove('active');
activeTooltip = null;
isMouseOverTooltip = false;
removeOverlay();
}
}
// 初始化(保持不变)
document.addEventListener('DOMContentLoaded', function() {
console.log('初始化 clab-tooltip');
const clabElements = document.querySelectorAll('.clab[data-options]');
console.log(`找到 ${clabElements.length} 个 .clab 元素`);
if (clabElements.length === 0) return;
// 加载内容
loadRnContent();
// 创建 tooltip
const tooltip = createTooltip();
// tooltip 鼠标进入/离开
tooltip.addEventListener('mouseenter', function() {
isMouseOverTooltip = true;
clearTimeout(hideTimer);
});
tooltip.addEventListener('mouseleave', function() {
isMouseOverTooltip = false;
hideTimer = setTimeout(() => {
forceHideTooltip();
}, 100);
});
// 为所有 .clab 元素添加事件监听
clabElements.forEach(clab => {
clab.addEventListener('mouseenter', function() {
clearTimeout(hideTimer);
setTimeout(() => {
if (isRnContentLoaded) {
showTooltip(this, tooltip);
}
}, 100);
});
clab.addEventListener('mouseleave', function() {
hideTimer = setTimeout(() => {
hideTooltip();
}, 100);
});
});
// ESC 键隐藏 tooltip
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && activeTooltip) {
forceHideTooltip();
}
});
// 点击其他地方隐藏 tooltip
document.addEventListener('click', function(e) {
if (activeTooltip && !activeTooltip.contains(e.target)) {
forceHideTooltip();
}
});
// 窗口大小改变时隐藏 tooltip
window.addEventListener('resize', forceHideTooltip);
// 滚动时隐藏 tooltip
window.addEventListener('scroll', forceHideTooltip);
});
})();