优化: - initArtalk 加节点级幂等守卫(mount.__atkInited,失败回滚), 消除 artalk.html/mypjax 多入口导致的 4× comments + 4× pv 重复请求 - fetch 包装层对无 body 的 GET/HEAD 去掉多余 content-type, 消掉 Artalk 对 GET /comments 的 CORS 预检(按完整 URL 缓存, 每篇文章都重付一次 RTT)。实测评论列表 +1150~1565ms → +626ms 修复: - 评论列表骨架屏改注入 .atk-list-body(原骨架在 #Comments 内, 被 Artalk.init 清空),list-loaded/故障/12s 兜底移除 - 友链申请弹窗抽成 modules/friendlink.js 进 page-only bundle (原内联脚本在 #pjax-container 外,pjax 后 flApplyOpenModal 未定义) - toast 统一走本地化 Message.js/Qmsg 适配层(保留 window.Toast/showToast 旧 API) 文档: - README / 架构总览 更新;新增 评论加载优化方案.md
145 lines
5.4 KiB
JavaScript
145 lines
5.4 KiB
JavaScript
function themeSweep(newTheme, callback) {
|
||
// View Transition API — best quality if available
|
||
if (document.startViewTransition) {
|
||
document.startViewTransition(function() { callback(); });
|
||
return;
|
||
}
|
||
|
||
// UIkit animate fallback
|
||
if (window.UIkit && UIkit.animate) {
|
||
var body = document.body;
|
||
UIkit.animate(body, 'uk-animation-fade uk-animation-fast');
|
||
setTimeout(function() {
|
||
callback();
|
||
UIkit.animate(body, 'uk-animation-fade uk-animation-fast');
|
||
}, 150);
|
||
return;
|
||
}
|
||
|
||
// Last resort: simple opacity
|
||
callback();
|
||
}
|
||
|
||
// Toast 快捷方式已统一迁到 modules/toast.js(Message.js/Qmsg 适配层),
|
||
// 这里不再重复定义 window.showToast,避免双实现漂移。
|
||
|
||
// Theme Toggle Logic
|
||
window.initThemeToggle = function() {
|
||
var avatar = document.getElementById('theme-toggle-avatar');
|
||
if (!avatar) return;
|
||
|
||
var newAvatar = avatar.cloneNode(true);
|
||
avatar.parentNode.replaceChild(newAvatar, avatar);
|
||
|
||
newAvatar.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
|
||
var html = document.documentElement;
|
||
var currentTheme = html.getAttribute('data-theme');
|
||
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||
|
||
var apply = function() {
|
||
html.setAttribute('data-theme', newTheme);
|
||
localStorage.setItem('theme', newTheme);
|
||
window.showToast('已切换至 ' + (newTheme === 'dark' ? '深色' : '浅色') + '模式', 'success');
|
||
if (window.artalk && typeof window.artalk.setDarkMode === 'function') {
|
||
window.artalk.setDarkMode(newTheme === 'dark');
|
||
}
|
||
window.dispatchEvent(new Event('resize'));
|
||
};
|
||
|
||
themeSweep(newTheme, apply);
|
||
});
|
||
};
|
||
|
||
// Code Folding Logic
|
||
window.initCodeFold = function() {
|
||
var codeBlocks = document.querySelectorAll('.highlight');
|
||
codeBlocks.forEach(function(block) {
|
||
if (block.parentNode && block.parentNode.classList.contains('code-fold-container')) return;
|
||
|
||
var pre = block.querySelector('pre');
|
||
if (!pre) return;
|
||
|
||
// Use pre's content height; 0 means layout not ready, skip
|
||
var contentHeight = pre.scrollHeight || block.scrollHeight;
|
||
if (!contentHeight || contentHeight <= 360) return;
|
||
|
||
var wrapper = document.createElement('div');
|
||
wrapper.className = 'code-fold-container collapsed';
|
||
block.parentNode.insertBefore(wrapper, block);
|
||
wrapper.appendChild(block);
|
||
|
||
var btn = document.createElement('button');
|
||
btn.type = 'button';
|
||
btn.className = 'code-expand-btn';
|
||
wrapper.appendChild(btn);
|
||
|
||
var syncButton = function() {
|
||
var collapsed = wrapper.classList.contains('collapsed');
|
||
btn.innerHTML = collapsed
|
||
? '<span><i class="ri-arrow-down-s-line" style="margin-right:4px;"></i> 展开代码 </span>'
|
||
: '<span><i class="ri-arrow-up-s-line" style="margin-right:4px;"></i> 收起代码 </span>';
|
||
};
|
||
|
||
btn.onclick = function() {
|
||
wrapper.classList.toggle('collapsed');
|
||
syncButton();
|
||
};
|
||
syncButton();
|
||
});
|
||
};
|
||
|
||
// Code Copy Logic
|
||
window.initCodeCopy = function() {
|
||
var codeBlocks = document.querySelectorAll('pre');
|
||
|
||
codeBlocks.forEach(function(pre) {
|
||
if (pre.parentNode.querySelector('.copy-code-btn')) return;
|
||
if (pre.querySelector('.copy-code-btn')) return;
|
||
|
||
var btn = document.createElement('button');
|
||
btn.className = 'copy-code-btn';
|
||
btn.innerHTML = '<i class="ri-file-copy-line"></i>';
|
||
btn.title = "复制代码";
|
||
btn.style.cssText =
|
||
'position:absolute;top:-6px;right:5px;background:transparent;border:none;' +
|
||
'color:#888;cursor:pointer;padding:5px;border-radius:4px;' +
|
||
'transition:all 0.2s;opacity:0;z-index:10;';
|
||
|
||
pre.addEventListener('mouseenter', function() { btn.style.opacity = '1'; });
|
||
pre.addEventListener('mouseleave', function() { btn.style.opacity = '0'; });
|
||
|
||
if (typeof window.isMobile === 'function' && window.isMobile()) { btn.style.opacity = '1'; }
|
||
|
||
btn.addEventListener('click', function(e) {
|
||
e.stopPropagation();
|
||
var code = pre.querySelector('code') ? pre.querySelector('code').innerText : pre.innerText;
|
||
|
||
if (navigator.clipboard) {
|
||
navigator.clipboard.writeText(code).then(function() {
|
||
window.showToast('代码已复制 / Copied!', 'success');
|
||
btn.innerHTML = '<i class="ri-check-line"></i>';
|
||
setTimeout(function() { btn.innerHTML = '<i class="ri-file-copy-line"></i>'; }, 2000);
|
||
}).catch(function() {
|
||
window.showToast('复制失败 / Failed', 'error');
|
||
});
|
||
} else {
|
||
var textarea = document.createElement('textarea');
|
||
textarea.value = code;
|
||
document.body.appendChild(textarea);
|
||
textarea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(textarea);
|
||
window.showToast('代码已复制 / Copied!', 'success');
|
||
}
|
||
});
|
||
|
||
if (getComputedStyle(pre).position === 'static') {
|
||
pre.style.position = 'relative';
|
||
}
|
||
|
||
pre.appendChild(btn);
|
||
});
|
||
};
|