Files
blog/themes/Ying/assets/js/modules/artalk.js
T

230 lines
10 KiB
JavaScript
Raw Normal View History

2026-01-31 19:24:22 +08:00
// Global Artalk Initialization Function (Supports PJAX)
2026-05-25 00:03:50 +08:00
window.loadScriptOnce = window.loadScriptOnce || function(src, globalName, callback) {
if (globalName && window[globalName]) {
callback();
return;
}
var existing = document.querySelector('script[data-dynamic-src="' + src + '"]');
if (existing) {
existing.addEventListener('load', callback, { once: true });
return;
}
var script = document.createElement('script');
script.src = src;
script.defer = true;
script.dataset.dynamicSrc = src;
script.onload = callback;
document.head.appendChild(script);
};
2026-01-31 19:24:22 +08:00
window.initArtalk = function() {
if (!document.querySelector("#Comments")) return;
2026-05-25 00:03:50 +08:00
2026-01-31 19:24:22 +08:00
// Check if configuration exists
if (!window.artalkConfig) {
console.warn('window.artalkConfig is not defined. Skipping Artalk init.');
return;
}
if (typeof Artalk === 'undefined') {
2026-05-25 00:03:50 +08:00
var artalkUrl = window.optionalAssetUrls && window.optionalAssetUrls.artalk;
if (!artalkUrl) return;
window.loadScriptOnce(artalkUrl, 'Artalk', window.initArtalk);
2026-01-31 19:24:22 +08:00
return;
}
2026-05-25 00:03:50 +08:00
2026-01-31 19:24:22 +08:00
try {
2026-01-31 22:06:19 +08:00
// Logic for caching Artalk configuration to speed up rendering
// 1. Manual Refresh (F5): Request from server and save to localStorage
// 2. PJAX: Load from localStorage if available
// Initialize initialization count to track PJAX usage
if (typeof window.artalkInitCount === 'undefined') {
window.artalkInitCount = 0;
}
window.artalkInitCount++;
var config = Object.assign({}, window.artalkConfig); // Clone config
var server = config.server.replace(/\/$/, '');
var confKey = 'artalk_conf_' + btoa(server);
var cachedConf = null;
// Try to read cache
try {
var raw = localStorage.getItem(confKey);
if (raw) cachedConf = JSON.parse(raw);
} catch(e) {
console.warn('Failed to parse Artalk cache:', e);
}
// Decision: Use Cache or Fetch?
// Use cache only if:
// 1. Not the first load (PJAX)
// 2. Cache exists
if (window.artalkInitCount > 1 && cachedConf) {
// Merge cache into config
// CAUTION: cachedConf contains page-specific data (like pageKey) from the previous page.
// We must ensure current window.artalkConfig overwrites it.
try {
// Defensive copy to avoid mutating cached object
var mergedConfig = Object.assign({}, config);
Object.assign(mergedConfig, cachedConf);
Object.assign(mergedConfig, window.artalkConfig); // Re-apply current page config
mergedConfig.useBackendConf = false;
// Attempt to init with cache
window.artalk = Artalk.init(mergedConfig);
} catch (initErr) {
console.warn('Artalk init with cache failed, falling back to default:', initErr);
localStorage.removeItem(confKey); // Clear bad cache
window.artalk = Artalk.init(config); // Fallback to normal init
}
} else {
// First load or no cache: Allow Artalk to fetch (useBackendConf defaults to true)
window.artalk = Artalk.init(config);
}
var artalk = window.artalk; // Local reference
2026-02-05 21:53:08 +08:00
// Sync Theme Mode Immediately
var currentTheme = document.documentElement.getAttribute('data-theme');
if (typeof artalk.setDarkMode === 'function') {
artalk.setDarkMode(currentTheme === 'dark');
}
2026-01-31 22:06:19 +08:00
// Aggressive Config Saving Strategy
var saveConf = function() {
try {
// Attempt to find the full configuration object
// artalk.conf is the standard property in v2+
// artalk.ctx.conf is for internal context access
var remoteConf = artalk.conf || (artalk.ctx && artalk.ctx.conf);
if (remoteConf) {
2026-01-31 22:52:19 +08:00
// Filter out non-serializable properties (like DOM elements, circular refs)
var safeConf = {};
for (var key in remoteConf) {
var val = remoteConf[key];
// Skip if value is a DOM element
if (val instanceof Element || val instanceof Node) continue;
// Skip if value is window or document
if (val === window || val === document) continue;
// Skip internal private properties starting with $ or _
if (key.startsWith('$') || key.startsWith('_')) continue;
// Only allow simple types or simple objects
var type = typeof val;
if (type === 'string' || type === 'number' || type === 'boolean' || val === null) {
safeConf[key] = val;
} else if (type === 'object') {
// Simple array check
if (Array.isArray(val)) {
safeConf[key] = val;
} else {
// For objects, deep check might be too expensive/complex here.
// We'll try to shallow copy if it looks like a plain object
// and doesn't have circular markers like 'el'
if (val.constructor === Object && !val.el && !val.$el) {
safeConf[key] = val;
}
}
}
}
localStorage.setItem(confKey, JSON.stringify(safeConf));
2026-01-31 22:06:19 +08:00
// console.log('Artalk: Config cached to', confKey);
}
} catch(e) {
console.error('Artalk saveConf error:', e);
}
};
// Hook into multiple events to ensure we capture the config after it's loaded
// 'conf-loaded' is the ideal event if supported
artalk.on('conf-loaded', saveConf);
2026-05-24 19:58:07 +08:00
2026-01-31 22:06:19 +08:00
// 'mounted' is when the widget is ready
artalk.on('mounted', saveConf);
// 'list-loaded' means comments (and likely config) are fetched
artalk.on('list-loaded', saveConf);
// Fallback: Poll check after a few seconds (for slow networks)
setTimeout(saveConf, 2000);
setTimeout(saveConf, 5000);
2026-01-31 19:24:22 +08:00
// Handle list-loaded event for lazy loading avatars and animations
artalk.on('list-loaded', function() {
if (window.artalkConfig.aiComment) {
var ai = window.artalkConfig.aiComment;
var listBody = document.querySelector('.atk-list .atk-list-body');
if (listBody && !listBody.querySelector('.atk-ai-comment')) {
var aiCommentHtml = `
<div class="atk-comment-wrap atk-comment atk-ai-comment" id="atk-comment-ai">
<div class="atk-avatar" uk-scrollspy="cls: uk-animation-scale-up; delay: 200">
<img src="${ai.avatar}" alt="${ai.name}">
</div>
<div class="atk-main" uk-scrollspy="cls: uk-animation-slide-right-small; delay: 300">
<div class="atk-header">
<span class="atk-nick" uk-scrollspy="cls: uk-animation-slide-top-small; delay: 350">${ai.name}</span>
2026-01-31 21:27:06 +08:00
<span class="atk-badge">${window.artalkConfig.aiBadge || 'AI'}</span>
2026-01-31 19:24:22 +08:00
<span class="atk-date">${ai.date}</span>
</div>
<div class="atk-body">
<div class="atk-content">${ai.content}</div>
</div>
</div>
</div>
`;
listBody.insertAdjacentHTML('afterbegin', aiCommentHtml);
// Hide "No Comment" placeholder if it exists
var noComment = document.querySelector('.atk-list-no-comment');
if (noComment) {
noComment.style.display = 'none';
}
}
}
// Add staggered animation to the comment list
var list = document.querySelector('.atk-list');
if (list) {
list.setAttribute('uk-scrollspy', 'target: > .atk-comment-wrap; cls: uk-animation-slide-bottom-small; delay: 50');
}
// Apply granular animations to inner elements
document.querySelectorAll('.atk-comment-wrap:not(.atk-ai-comment)').forEach(function(wrap) {
var avatar = wrap.querySelector('.atk-avatar');
if (avatar) avatar.setAttribute('uk-scrollspy', 'cls: uk-animation-scale-up; delay: 200');
var main = wrap.querySelector('.atk-main');
if (main) main.setAttribute('uk-scrollspy', 'cls: uk-animation-slide-right-small; delay: 300');
var nick = wrap.querySelector('.atk-nick');
if (nick) nick.setAttribute('uk-scrollspy', 'cls: uk-animation-slide-top-small; delay: 350');
});
// Find all avatars in the comment list
2026-01-31 21:27:06 +08:00
// We let lazyload.js handle the avatars automatically by calling lazyLoadAgain()
// which will find these new images, set the placeholder, and observe them.
2026-01-31 19:24:22 +08:00
// Trigger lazy load update
if (window.lazyLoadAgain) {
window.lazyLoadAgain();
}
2026-01-31 21:27:06 +08:00
// Force hide "No Comment" if AI comment exists
if (window.artalkConfig.aiComment) {
2026-05-25 00:03:50 +08:00
var noComment = document.querySelector('.atk-list-no-comment');
var aiComment = document.querySelector('.atk-ai-comment');
if (aiComment && noComment) {
noComment.style.display = 'none';
}
2026-01-31 21:27:06 +08:00
}
2026-01-31 19:24:22 +08:00
});
} catch (e) {
console.error('Artalk init failed:', e);
}
};