// Global Artalk Initialization Function (Supports PJAX) window.initArtalk = function() { if (!document.querySelector("#Comments")) return; // Check if configuration exists if (!window.artalkConfig) { console.warn('window.artalkConfig is not defined. Skipping Artalk init.'); return; } // Check if Artalk library is loaded if (typeof Artalk === 'undefined') { console.warn('Artalk library is not loaded yet. Waiting for bundle...'); return; } try { // 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 // Sync Theme Mode Immediately var currentTheme = document.documentElement.getAttribute('data-theme'); if (typeof artalk.setDarkMode === 'function') { artalk.setDarkMode(currentTheme === 'dark'); } // 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) { // 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)); // 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); // '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); // 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 = `
${ai.name}
${ai.name} ${window.artalkConfig.aiBadge || 'AI'} ${ai.date}
${ai.content}
`; 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 // We let lazyload.js handle the avatars automatically by calling lazyLoadAgain() // which will find these new images, set the placeholder, and observe them. // Trigger lazy load update if (window.lazyLoadAgain) { window.lazyLoadAgain(); } // Force hide "No Comment" if AI comment exists if (window.artalkConfig.aiComment) { var checkNoComment = setInterval(function() { var noComment = document.querySelector('.atk-list-no-comment'); var aiComment = document.querySelector('.atk-ai-comment'); if (aiComment && noComment && noComment.style.display !== 'none') { noComment.style.display = 'none'; } if (!document.querySelector('#Comments')) { clearInterval(checkNoComment); } }, 500); // Clear interval after 10 seconds to save resources setTimeout(function() { clearInterval(checkNoComment); }, 10000); } }); } catch (e) { console.error('Artalk init failed:', e); } };