微件:Card:修订间差异

来自卡厄思梦境WIKI
跳转到导航 跳转到搜索
律Rhyme留言 | 贡献
功能测试
律Rhyme留言 | 贡献
功能测试
 
(未显示同一用户的3个中间版本)
第1行: 第1行:
<noinclude>
<noinclude>
此Widget为卡牌显示添加交互效果:
此Widget用于为卡牌添加交互功能。
* 鼠标悬停时的动画效果
使用方法:在页面底部添加 {{#widget:Card}}
* 点击后显示放大的模态框
[[分类:Widget]]
* 自动检测spark_enable属性显示闪光按钮
* 支持闪光卡牌列表显示
* 预加载、批量加载和本地缓存优化
* 事件委托、DOM优化、批量API请求
 
使用方法:在页面中添加 {{#widget:Card}}
</noinclude><includeonly>
</noinclude><includeonly>
<style>
<style>
/* 卡牌容器 */
/* 卡牌悬停和点击动画 */
.card-wrapper {
    display: inline-block;
}
 
/* 卡牌悬停动画 */
.card-deck-trans {
.card-deck-trans {
     cursor: pointer;
     cursor: pointer;
     transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease;
     transition: transform 0.2s ease;
}
}


.card-deck-trans:hover {
.card-deck-trans:hover {
     transform: translateY(-8px);
     transform: scale(1.05);
     filter: brightness(1.1);
     z-index: 10;
}
}


.card-deck-trans:active {
.card-deck-trans:active {
     transform: translateY(-4px) scale(0.98);
     transform: scale(0.98);
}
}


/* 模态框通用样式 */
/* 模态框样式 */
.card-modal-overlay,
.card-modal-overlay {
.spark-modal-overlay {
     display: none;
     display: none;
     position: fixed;
     position: fixed;
第40行: 第28行:
     width: 100%;
     width: 100%;
     height: 100%;
     height: 100%;
     background-color: rgba(0, 0, 0, 0.85);
     background-color: rgba(0, 0, 0, 0.8);
     z-index: 9999;
     z-index: 9999;
     justify-content: center;
     justify-content: center;
     align-items: center;
     align-items: center;
     opacity: 0;
     flex-direction: column;
    transition: opacity 0.3s ease;
}
}


.spark-modal-overlay {
.card-modal-overlay.active {
    z-index: 10010;
    background-color: rgba(0, 0, 0, 0.9);
}
 
.card-modal-overlay.active,
.spark-modal-overlay.active {
     display: flex;
     display: flex;
    opacity: 1;
}
.card-modal-overlay.fade-in,
.spark-modal-overlay.fade-in {
    opacity: 1;
}
.card-modal-overlay.fade-out,
.spark-modal-overlay.fade-out {
    opacity: 0;
}
}


/* 关闭按钮 */
/* 关闭按钮 */
.card-modal-close,
.card-modal-close {
.spark-modal-close {
     position: absolute;
     position: fixed;
     top: 20px;
     top: 20px;
     right: 30px;
     right: 30px;
     font-size: 48px;
     font-size: 40px;
    font-weight: bold;
     color: #fff;
     color: #ffffff;
     cursor: pointer;
     cursor: pointer;
     z-index: 10001;
     z-index: 10001;
     line-height: 1;
     line-height: 1;
    transition: color 0.2s ease, transform 0.2s ease;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
}


.spark-modal-close {
.card-modal-close:hover {
    z-index: 10012;
}
 
.card-modal-close:hover,
.spark-modal-close:hover {
     color: #ff6b6b;
     color: #ff6b6b;
    transform: scale(1.1);
}
}


/* 模态框中的卡牌容器 */
/* 模态框内容容器 */
.card-modal-content {
.card-modal-content {
    position: relative;
    z-index: 10000;
     display: flex;
     display: flex;
     flex-direction: column;
     flex-direction: column;
     align-items: center;
     align-items: center;
     animation: cardZoomIn 0.3s ease;
     justify-content: center;
}
}


.spark-modal-content {
/* 放大后的卡牌容器 */
.card-modal-card {
     position: relative;
     position: relative;
    z-index: 10011;
     width: 270px;
    max-width: 90vw;
     height: 384px;
    max-height: 85vh;
     display: inline-block;
    overflow-y: auto;
    padding: 20px;
    animation: cardZoomIn 0.3s ease;
}
 
@keyframes cardZoomIn {
    from {
        transform: scale(0.8);
        opacity: 0;
    }
    to {
        transform: scale(1);
        opacity: 1;
    }
}
 
/* 放大后的卡牌样式 */
.card-modal-content > .card-deck-trans,
.card-modal-content > .card-wrapper > .card-deck-trans {
     width: 270px !important;
     height: 384px !important;
    transform: none !important;
    filter: none !important;
     cursor: default;
}
 
.card-modal-content > .card-deck-trans:hover,
.card-modal-content > .card-wrapper > .card-deck-trans:hover {
    transform: none !important;
    filter: none !important;
}
}


.card-modal-content > .card-deck-trans > .card-deck,
.card-modal-card .card-deck {
.card-modal-content > .card-wrapper > .card-deck-trans > .card-deck {
    position: absolute;
     transform: scale(0.6) !important;
    width: 450px;
    height: 640px;
     transform: scale(0.6);
    transform-origin: top left;
}
}


/* 闪光按钮 */
/* 闪光按钮 */
.spark-button {
.card-spark-button {
     margin-top: 20px;
     margin-top: 20px;
     padding: 12px 36px;
     padding: 10px 30px;
     font-size: 18px;
     background-color: #4a4a4a;
    font-weight: bold;
     color: #ffd700;
    color: #fff;
     border: 1px solid #ffd700;
     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     border-radius: 4px;
     border: 2px solid #a78bfa;
     border-radius: 8px;
     cursor: pointer;
     cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
    text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.spark-button:hover {
    background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
    box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
    transform: translateY(-2px);
}
.spark-button:active {
    transform: translateY(0);
    box-shadow: 0 2px 10px rgba(102, 126, 234, 0.4);
}
/* 闪光按钮状态 */
.spark-button.loading {
    opacity: 0.7;
    cursor: wait;
}
.spark-button.cached::after {
    content: " ✓";
    color: #90EE90;
}
/* 闪光图标动画 */
.spark-button:not(.loading)::before {
    content: "✦ ";
    animation: sparkle 1.5s ease-in-out infinite;
}
@keyframes sparkle {
    0%, 100% { opacity: 0.5; }
    50% { opacity: 1; }
}
/* 闪光卡牌列表样式 */
.spark-modal-title {
    text-align: center;
    color: #fff;
    font-size: 24px;
    font-weight: bold;
    margin-bottom: 20px;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
.spark-modal-title::before {
    content: "✦ ";
    color: #a78bfa;
}
.spark-modal-title::after {
    content: " ✦";
    color: #a78bfa;
}
.spark-card-list {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 15px;
}
.spark-card-list .card-wrapper {
    display: inline-block;
}
.spark-card-list .card-deck-trans {
    cursor: pointer;
}
.spark-card-list .card-deck-trans:hover {
    transform: translateY(-8px);
    filter: brightness(1.1);
}
/* 加载提示 */
.spark-loading {
    color: #fff;
    font-size: 18px;
    text-align: center;
    padding: 40px;
}
.spark-loading::after {
    content: "";
    animation: loadingDots 1.5s infinite;
}
@keyframes loadingDots {
    0% { content: ""; }
    25% { content: "."; }
    50% { content: ".."; }
    75% { content: "..."; }
}
/* 空列表提示 */
.spark-empty {
    color: #999;
     font-size: 16px;
     font-size: 16px;
    text-align: center;
    padding: 40px;
}
}


/* 缓存状态提示 */
.card-spark-button:hover {
.spark-cache-hint {
     background-color: #5a5a5a;
     color: #666;
    font-size: 12px;
    text-align: center;
    margin-top: 15px;
}
}


/* 放大的闪光卡牌 */
/* 闪光卡牌模态框 */
.spark-enlarged-container {
.spark-modal-overlay {
    display: none;
     position: fixed;
     position: fixed;
     top: 0;
     top: 0;
第279行: 第103行:
     width: 100%;
     width: 100%;
     height: 100%;
     height: 100%;
     display: flex;
     background-color: rgba(0, 0, 0, 0.9);
    z-index: 10000;
     justify-content: center;
     justify-content: center;
     align-items: center;
     align-items: flex-start;
     background: rgba(0, 0, 0, 0.95);
     overflow-y: auto;
     z-index: 10015;
     padding: 60px 20px 20px 20px;
     animation: cardZoomIn 0.3s ease;
     box-sizing: border-box;
}
}


.spark-enlarged-card {
.spark-modal-overlay.active {
    position: relative;
     display: flex;
     display: flex;
    flex-direction: column;
    align-items: center;
}
}


.spark-enlarged-card .card-deck-trans {
.spark-modal-close {
    width: 270px !important;
    height: 384px !important;
    transform: none !important;
    filter: none !important;
    cursor: default;
}
 
.spark-enlarged-card .card-deck-trans:hover {
    transform: none !important;
    filter: none !important;
}
 
.spark-enlarged-card .card-deck {
    transform: scale(0.6) !important;
}
 
.spark-enlarged-close {
     position: fixed;
     position: fixed;
     top: 20px;
     top: 20px;
     right: 30px;
     right: 30px;
     font-size: 48px;
     font-size: 40px;
    font-weight: bold;
     color: #fff;
     color: #ffffff;
     cursor: pointer;
     cursor: pointer;
     z-index: 10016;
     z-index: 10002;
     line-height: 1;
     line-height: 1;
    transition: color 0.2s ease, transform 0.2s ease;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}
}


.spark-enlarged-close:hover {
.spark-modal-close:hover {
     color: #ff6b6b;
     color: #ff6b6b;
    transform: scale(1.1);
}
}


/* 防止模态框打开时页面滚动 */
.spark-modal-content {
body.card-modal-open {
     display: flex;
     overflow: hidden;
    flex-wrap: wrap;
    justify-content: center;
    gap: 20px;
    max-width: 1200px;
}
}


/* 滚动条样式 */
.spark-modal-content .card-deck-trans {
.spark-modal-content::-webkit-scrollbar {
     cursor: default;
     width: 8px;
    pointer-events: none;
}
}


.spark-modal-content::-webkit-scrollbar-track {
.spark-modal-content .card-deck-trans:hover {
     background: rgba(255, 255, 255, 0.1);
     transform: none;
    border-radius: 4px;
}
}


.spark-modal-content::-webkit-scrollbar-thumb {
.spark-modal-title {
     background: rgba(255, 255, 255, 0.3);
    width: 100%;
     border-radius: 4px;
    text-align: center;
    color: #ffd700;
     font-size: 24px;
     margin-bottom: 20px;
}
}


.spark-modal-content::-webkit-scrollbar-thumb:hover {
.spark-loading {
     background: rgba(255, 255, 255, 0.5);
    color: #fff;
     font-size: 18px;
}
}
</style>
</style>
<div id="card-modal-overlay" class="card-modal-overlay">
    <span class="card-modal-close" id="card-modal-close">&times;</span>
    <div class="card-modal-content" id="card-modal-content"></div>
</div>
<div id="spark-modal-overlay" class="spark-modal-overlay">
    <span class="spark-modal-close" id="spark-modal-close">&times;</span>
    <div class="spark-modal-content" id="spark-modal-content"></div>
</div>


<script>
<script>
第359行: 第176行:
     'use strict';
     'use strict';
      
      
    // ==================== 等待 MediaWiki 加载 ====================
     function init() {
     function waitForMw(callback) {
         var cardModal = document.getElementById('card-modal-overlay');
         // 检查 mw 和 mw.loader 是否存在
         var cardModalContent = document.getElementById('card-modal-content');
        if (typeof mw !== 'undefined' && mw.loader && typeof mw.loader.using === 'function') {
        var cardModalClose = document.getElementById('card-modal-close');
            mw.loader.using(['mediawiki.api']).then(callback);
         var sparkModal = document.getElementById('spark-modal-overlay');
        } else if (typeof mw !== 'undefined' && mw.Api) {
         var sparkModalContent = document.getElementById('spark-modal-content');
            // mw.Api 已经可用
        var sparkModalClose = document.getElementById('spark-modal-close');
            callback();
         } else {
            // 等待 mw 加载
            var checkCount = 0;
            var maxChecks = 50; // 最多等待5秒
           
            var checkInterval = setInterval(function() {
                checkCount++;
               
                if (typeof mw !== 'undefined' && mw.loader && typeof mw.loader.using === 'function') {
                    clearInterval(checkInterval);
                    mw.loader.using(['mediawiki.api']).then(callback);
                } else if (typeof mw !== 'undefined' && mw.Api) {
                    clearInterval(checkInterval);
                    callback();
                } else if (checkCount >= maxChecks) {
                    clearInterval(checkInterval);
                    console.warn('CardWidget: MediaWiki API 加载超时,闪光功能可能不可用');
                    // 仍然初始化基本功能
                    callback();
                }
            }, 100);
        }
    }
   
    // ==================== 主程序 ====================
    function initCardWidget() {
       
        // ==================== API 客户端 ====================
        var apiClient = null;
         var apiAvailable = false;
       
        function getApi() {
            if (!apiAvailable) {
                // 检查 API 是否可用
                if (typeof mw !== 'undefined' && typeof mw.Api === 'function') {
                    apiAvailable = true;
                } else {
                    return null;
                }
            }
           
            if (!apiClient && apiAvailable) {
                try {
                    apiClient = new mw.Api();
                } catch (e) {
                    console.warn('CardWidget: 无法创建 API 客户端', e);
                    apiAvailable = false;
                    return null;
                }
            }
            return apiClient;
        }
       
        // ==================== 缓存管理器 ====================
         var SparkCache = (function() {
            var CACHE_KEY = 'spark_card_cache_v2';
            var CACHE_EXPIRE = 24 * 60 * 60 * 1000;
           
            var memoryCache = null;
            var isDirty = false;
            var saveTimer = null;
           
            var storageAvailable = (function() {
                try {
                    var test = '__storage_test__';
                    localStorage.setItem(test, test);
                    localStorage.removeItem(test);
                    return true;
                } catch (e) {
                    return false;
                }
            })();
           
            function loadFromStorage() {
                if (memoryCache !== null) return memoryCache;
               
                if (!storageAvailable) {
                    memoryCache = {};
                    return memoryCache;
                }
               
                try {
                    var data = localStorage.getItem(CACHE_KEY);
                    memoryCache = data ? JSON.parse(data) : {};
                } catch (e) {
                    memoryCache = {};
                }
                return memoryCache;
            }
           
            function scheduleSave() {
                if (!storageAvailable || saveTimer) return;
               
                saveTimer = setTimeout(function() {
                    saveTimer = null;
                    if (isDirty && memoryCache) {
                        try {
                            localStorage.setItem(CACHE_KEY, JSON.stringify(memoryCache));
                            isDirty = false;
                        } catch (e) {
                            cleanup(true);
                        }
                    }
                }, 1000);
            }
           
            function cleanup(force) {
                var cache = loadFromStorage();
                var now = Date.now();
                var hasChanges = false;
                var keys = Object.keys(cache);
               
                if (force && keys.length > 10) {
                    var items = keys.map(function(k) {
                        return { key: k, time: cache[k].timestamp };
                    }).sort(function(a, b) {
                        return a.time - b.time;
                    });
                   
                    var deleteCount = Math.floor(items.length / 2);
                    for (var i = 0; i < deleteCount; i++) {
                        delete cache[items[i].key];
                        hasChanges = true;
                    }
                } else {
                    for (var cardId in cache) {
                        if (cache.hasOwnProperty(cardId) && now - cache[cardId].timestamp > CACHE_EXPIRE) {
                            delete cache[cardId];
                            hasChanges = true;
                        }
                    }
                }
               
                if (hasChanges) {
                    isDirty = true;
                    scheduleSave();
                }
            }
           
            return {
                get: function(cardId) {
                    var cache = loadFromStorage();
                    var item = cache[cardId];
                   
                    if (!item) return null;
                    if (Date.now() - item.timestamp > CACHE_EXPIRE) {
                        delete cache[cardId];
                        isDirty = true;
                        scheduleSave();
                        return null;
                    }
                   
                    return item.html;
                },
               
                set: function(cardId, html) {
                    var cache = loadFromStorage();
                    cache[cardId] = {
                        html: html,
                        timestamp: Date.now()
                    };
                    isDirty = true;
                    scheduleSave();
                },
               
                has: function(cardId) {
                    var cache = loadFromStorage();
                    var item = cache[cardId];
                    return item && (Date.now() - item.timestamp <= CACHE_EXPIRE);
                },
               
                cleanup: cleanup,
               
                flush: function() {
                    if (!storageAvailable) return;
                   
                    if (saveTimer) {
                        clearTimeout(saveTimer);
                        saveTimer = null;
                    }
                    if (isDirty && memoryCache) {
                        try {
                            localStorage.setItem(CACHE_KEY, JSON.stringify(memoryCache));
                            isDirty = false;
                        } catch (e) {}
                    }
                }
            };
        })();
          
          
         // ==================== 批量API请求管理器 ====================
         function bindCardEvents() {
        var BatchRequestManager = (function() {
             var cards = document.querySelectorAll('.card-deck-trans');
             var pendingRequests = {};
            var requestQueue = [];
            var isProcessing = false;
            var batchSize = 5;
            var batchDelay = 50;
              
              
             function processBatch() {
             cards.forEach(function(card) {
                 if (isProcessing || requestQueue.length === 0) return;
                 if (card.dataset.bound || card.closest('.spark-modal-content') || card.closest('.card-modal-content')) {
               
                var api = getApi();
                if (!api) {
                    // API 不可用,清空队列并拒绝所有请求
                    requestQueue.forEach(function(cardId) {
                        var callbacks = pendingRequests[cardId] || [];
                        delete pendingRequests[cardId];
                        callbacks.forEach(function(cb) {
                            cb.reject(new Error('API not available'));
                        });
                    });
                    requestQueue = [];
                     return;
                     return;
                 }
                 }
                  
                  
                 isProcessing = true;
                 card.dataset.bound = 'true';
                  
                  
                 var batch = requestQueue.splice(0, batchSize);
                 card.addEventListener('click', function(e) {
                var promises = batch.map(function(cardId) {
                     e.preventDefault();
                    return fetchSparkList(cardId, api);
                     e.stopPropagation();
                });
               
                Promise.all(promises).then(function() {
                    isProcessing = false;
                    if (requestQueue.length > 0) {
                        setTimeout(processBatch, batchDelay);
                    }
                });
            }
           
            function fetchSparkList(cardId, api) {
                return new Promise(function(resolve) {
                    api.get({
                        action: 'parse',
                        text: '{{Card/spark/list|' + cardId + '}}',
                        prop: 'text',
                        disablelimitreport: true,
                        format: 'json'
                    }).done(function(data) {
                        var html = '';
                        if (data.parse && data.parse.text) {
                            html = data.parse.text['*'];
                            SparkCache.set(cardId, html);
                        }
                       
                        var callbacks = pendingRequests[cardId] || [];
                        delete pendingRequests[cardId];
                        callbacks.forEach(function(cb) {
                            cb.resolve(html);
                        });
                       
                        resolve();
                     }).fail(function(error) {
                        var callbacks = pendingRequests[cardId] || [];
                        delete pendingRequests[cardId];
                        callbacks.forEach(function(cb) {
                            cb.reject(error);
                        });
                       
                        resolve();
                     });
                });
            }
           
            return {
                request: function(cardId) {
                    return new Promise(function(resolve, reject) {
                        var cached = SparkCache.get(cardId);
                        if (cached !== null) {
                            resolve(cached);
                            return;
                        }
                       
                        if (pendingRequests[cardId]) {
                            pendingRequests[cardId].push({ resolve: resolve, reject: reject });
                            return;
                        }
                       
                        pendingRequests[cardId] = [{ resolve: resolve, reject: reject }];
                        requestQueue.push(cardId);
                       
                        if (!isProcessing) {
                            setTimeout(processBatch, 10);
                        }
                    });
                },
               
                preload: function(cardIds) {
                    if (!getApi()) return; // API 不可用时不预加载
                      
                      
                     var toLoad = cardIds.filter(function(cardId) {
                     var cardDeck = this.querySelector('.card-deck');
                        return !SparkCache.has(cardId) &&
                     if (!cardDeck) return;
                              !pendingRequests[cardId] &&
                              requestQueue.indexOf(cardId) === -1;
                     });
                      
                      
                     if (toLoad.length === 0) return;
                     var clonedCard = document.createElement('div');
                    clonedCard.className = 'card-modal-card';
                    clonedCard.innerHTML = '<div class="card-deck">' + cardDeck.innerHTML + '</div>';
                      
                      
                     toLoad.forEach(function(cardId) {
                     cardModalContent.innerHTML = '';
                        pendingRequests[cardId] = [];
                    cardModalContent.appendChild(clonedCard);
                        requestQueue.push(cardId);
                    });
                      
                      
                    if (!isProcessing) {
                     var sparkEnable = this.dataset.sparkEnable;
                        setTimeout(processBatch, 100);
                     var sort = this.dataset.sort;
                    }
                     var cardId = this.dataset.cardId;
                },
               
                prioritize: function(cardId) {
                     var idx = requestQueue.indexOf(cardId);
                    if (idx > 0) {
                        requestQueue.splice(idx, 1);
                        requestQueue.unshift(cardId);
                     }
                }
            };
        })();
       
        // ==================== DOM 管理器 ====================
        var DOMManager = (function() {
            var modalOverlay = null;
            var sparkModalOverlay = null;
            var modalContent = null;
            var sparkModalContent = null;
            var currentEnlargedContainer = null;
            var processedCards = new WeakSet();
           
            function createModals() {
                if (!modalOverlay) {
                    modalOverlay = document.createElement('div');
                    modalOverlay.className = 'card-modal-overlay';
                     modalOverlay.innerHTML = '<span class="card-modal-close">&times;</span><div class="card-modal-content"></div>';
                    document.body.appendChild(modalOverlay);
                    modalContent = modalOverlay.querySelector('.card-modal-content');
                }
               
                if (!sparkModalOverlay) {
                    sparkModalOverlay = document.createElement('div');
                    sparkModalOverlay.className = 'spark-modal-overlay';
                    sparkModalOverlay.innerHTML = '<span class="spark-modal-close">&times;</span><div class="spark-modal-content"></div>';
                    document.body.appendChild(sparkModalOverlay);
                    sparkModalContent = sparkModalOverlay.querySelector('.spark-modal-content');
                }
            }
           
            function closeModal() {
                if (!modalOverlay) return;
               
                modalOverlay.classList.add('fade-out');
                setTimeout(function() {
                    modalOverlay.classList.remove('active', 'fade-in', 'fade-out');
                    modalContent.innerHTML = '';
                    document.body.classList.remove('card-modal-open');
                }, 300);
            }
           
            function closeSparkModal() {
                closeEnlargedCard();
               
                if (!sparkModalOverlay) return;
               
                sparkModalOverlay.classList.add('fade-out');
                setTimeout(function() {
                    sparkModalOverlay.classList.remove('active', 'fade-in', 'fade-out');
                    sparkModalContent.innerHTML = '';
                }, 300);
            }
           
            function closeEnlargedCard() {
                if (currentEnlargedContainer) {
                    currentEnlargedContainer.remove();
                    currentEnlargedContainer = null;
                }
            }
           
            function openModal(cardElement, cardId, sparkEnable) {
                createModals();
               
                var cardClone = cardElement.cloneNode(true);
                modalContent.innerHTML = '';
                modalContent.appendChild(cardClone);
               
                // 只有在 API 可用且启用闪光时才添加按钮
                if (sparkEnable && sparkEnable.trim() !== '' && getApi()) {
                    var sparkButton = document.createElement('button');
                    sparkButton.className = 'spark-button';
                    sparkButton.textContent = '闪光';
                    sparkButton.dataset.cardId = cardId;
                      
                      
                     if (SparkCache.has(cardId)) {
                     if (sparkEnable === '1' && sort !== '中立卡牌' && sort !== '怪物卡牌' && cardId) {
                         sparkButton.classList.add('cached');
                        var sparkButton = document.createElement('button');
                    } else {
                        sparkButton.className = 'card-spark-button';
                         BatchRequestManager.prioritize(cardId);
                         sparkButton.textContent = '闪光';
                        sparkButton.addEventListener('click', function(e) {
                            e.stopPropagation();
                            openSparkModal(cardId);
                        });
                         cardModalContent.appendChild(sparkButton);
                     }
                     }
                      
                      
                     modalContent.appendChild(sparkButton);
                     cardModal.classList.add('active');
                }
                    document.body.style.overflow = 'hidden';
               
                modalOverlay.classList.add('active');
                document.body.classList.add('card-modal-open');
               
                requestAnimationFrame(function() {
                    modalOverlay.classList.add('fade-in');
                 });
                 });
             }
             });
           
         }
            function openSparkModal(cardId) {
                createModals();
               
                var cachedHtml = SparkCache.get(cardId);
               
                if (cachedHtml) {
                    showSparkContent(cachedHtml, true);
                } else {
                    sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-loading">加载中</div>';
                   
                    sparkModalOverlay.classList.add('active');
                    requestAnimationFrame(function() {
                        sparkModalOverlay.classList.add('fade-in');
                    });
                   
                    BatchRequestManager.request(cardId).then(function(html) {
                        showSparkContent(html, false);
                    }).catch(function() {
                        sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-empty">加载失败,请重试</div>';
                    });
                }
            }
           
            function showSparkContent(html, fromCache) {
                var tempDiv = document.createElement('div');
                tempDiv.innerHTML = html;
                var cards = tempDiv.querySelectorAll('.card-deck-trans');
               
                var cacheHint = fromCache ? '<div class="spark-cache-hint">已从缓存加载</div>' : '';
               
                if (cards.length > 0) {
                    sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div>' + html + cacheHint;
                } else {
                    sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-empty">暂无闪光卡牌</div>' + cacheHint;
                }
               
                if (!sparkModalOverlay.classList.contains('active')) {
                    sparkModalOverlay.classList.add('active');
                    requestAnimationFrame(function() {
                        sparkModalOverlay.classList.add('fade-in');
                    });
                }
            }
           
            function showEnlargedCard(cardElement) {
                closeEnlargedCard();
               
                var enlargedContainer = document.createElement('div');
                enlargedContainer.className = 'spark-enlarged-container';
               
                var closeBtn = document.createElement('span');
                closeBtn.className = 'spark-enlarged-close';
                closeBtn.innerHTML = '&times;';
                enlargedContainer.appendChild(closeBtn);
               
                var cardContent = document.createElement('div');
                cardContent.className = 'spark-enlarged-card';
                cardContent.appendChild(cardElement.cloneNode(true));
                enlargedContainer.appendChild(cardContent);
               
                document.body.appendChild(enlargedContainer);
                currentEnlargedContainer = enlargedContainer;
            }
           
            function processNewCards(nodes) {
                var sparkCardIds = [];
               
                nodes.forEach(function(node) {
                    if (node.nodeType !== 1) return;
                   
                    var cards = [];
                    if (node.classList && node.classList.contains('card-deck-trans')) {
                        cards.push(node);
                    }
                    if (node.querySelectorAll) {
                        var found = node.querySelectorAll('.card-deck-trans');
                        for (var i = 0; i < found.length; i++) {
                            cards.push(found[i]);
                        }
                    }
                   
                    cards.forEach(function(card) {
                        if (processedCards.has(card)) return;
                        if (card.closest('.card-modal-overlay') ||
                            card.closest('.spark-modal-overlay') ||
                            card.closest('.spark-enlarged-container')) return;
                       
                        processedCards.add(card);
                       
                        var sparkEnable = card.dataset.sparkEnable;
                        if (sparkEnable && sparkEnable.trim() !== '') {
                            var wrapper = card.closest('.card-wrapper');
                            var cardId = wrapper ? wrapper.dataset.cardId : '';
                            if (cardId && sparkCardIds.indexOf(cardId) === -1) {
                                sparkCardIds.push(cardId);
                            }
                        }
                    });
                });
               
                if (sparkCardIds.length > 0) {
                    setTimeout(function() {
                        BatchRequestManager.preload(sparkCardIds);
                    }, 2000);
                }
            }
           
            function initExistingCards() {
                var cards = document.querySelectorAll('.card-deck-trans');
                var nodeArray = [];
                cards.forEach(function(card) {
                    nodeArray.push(card);
                });
                processNewCards(nodeArray);
            }
           
            return {
                createModals: createModals,
                closeModal: closeModal,
                closeSparkModal: closeSparkModal,
                closeEnlargedCard: closeEnlargedCard,
                openModal: openModal,
                openSparkModal: openSparkModal,
                showEnlargedCard: showEnlargedCard,
                processNewCards: processNewCards,
                initExistingCards: initExistingCards,
                getModalOverlay: function() { return modalOverlay; },
                getSparkModalOverlay: function() { return sparkModalOverlay; }
            };
         })();
          
          
         // ==================== 事件委托管理器 ====================
         function openSparkModal(cardId) {
        var EventManager = (function() {
             sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div><div class="spark-loading">加载中...</div>';
             var hoverTimer = null;
            sparkModal.classList.add('active');
              
              
             function getCardInfo(cardElement) {
             var apiUrl = mw.config.get('wgScriptPath') + '/api.php';
                var wrapper = cardElement.closest('.card-wrapper');
            var params = new URLSearchParams({
                return {
                action: 'parse',
                    cardId: wrapper ? wrapper.dataset.cardId : '',
                text: '{{Card/display/spark|' + cardId + '}}',
                    sparkEnable: cardElement.dataset.sparkEnable || ''
                 prop: 'text',
                };
                 format: 'json',
            }
                 contentmodel: 'wikitext'
           
            });
            function isValidCardClick(target) {
                var card = target.closest('.card-deck-trans');
                 if (!card) return null;
               
                var inMainModal = card.closest('.card-modal-content');
                 var inSparkList = card.closest('.spark-card-list');
                 var inEnlarged = card.closest('.spark-enlarged-container');
               
                if (inMainModal && !inSparkList) return null;
                if (inEnlarged) return null;
               
                return card;
            }
              
              
             function init() {
             fetch(apiUrl + '?' + params.toString())
                document.addEventListener('click', function(e) {
                .then(function(response) {
                    var target = e.target;
                    return response.json();
                   
                })
                    if (target.classList.contains('card-modal-close')) {
                .then(function(data) {
                        e.stopPropagation();
                     if (data.parse && data.parse.text) {
                        DOMManager.closeModal();
                         var html = data.parse.text['*'];
                        return;
                         if (html.trim() === '' || html.indexOf('card-deck-trans') === -1) {
                    }
                            sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div><div style="color: #fff;">暂无闪光卡牌</div>';
                   
                    if (target.classList.contains('spark-modal-close')) {
                        e.stopPropagation();
                        DOMManager.closeSparkModal();
                        return;
                    }
                   
                    if (target.classList.contains('spark-enlarged-close')) {
                        e.stopPropagation();
                        DOMManager.closeEnlargedCard();
                        return;
                    }
                   
                     if (target.classList.contains('spark-button')) {
                        e.preventDefault();
                         var cardId = target.dataset.cardId;
                        if (cardId) {
                            DOMManager.openSparkModal(cardId);
                        }
                        return;
                    }
                   
                    if (target.classList.contains('card-modal-overlay')) {
                        DOMManager.closeModal();
                         return;
                    }
                   
                    if (target.classList.contains('spark-modal-overlay')) {
                        DOMManager.closeSparkModal();
                        return;
                    }
                   
                    if (target.classList.contains('spark-enlarged-container')) {
                        DOMManager.closeEnlargedCard();
                        return;
                    }
                   
                    var card = isValidCardClick(target);
                    if (card) {
                        e.preventDefault();
                       
                        var inSparkList = card.closest('.spark-card-list');
                        var info = getCardInfo(card);
                       
                        if (inSparkList) {
                            DOMManager.showEnlargedCard(card);
                         } else {
                         } else {
                             DOMManager.openModal(card, info.cardId, info.sparkEnable);
                             sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div>' + html;
                        }
                    }
                }, true);
               
                document.addEventListener('mouseenter', function(e) {
                    var card = e.target.closest && e.target.closest('.card-deck-trans');
                    if (!card) return;
                   
                    if (card.closest('.card-modal-overlay') ||
                        card.closest('.spark-modal-overlay') ||
                        card.closest('.spark-enlarged-container')) return;
                   
                    var info = getCardInfo(card);
                    if (info.sparkEnable && info.sparkEnable.trim() !== '' && info.cardId) {
                        if (hoverTimer) {
                            clearTimeout(hoverTimer);
                        }
                       
                        hoverTimer = setTimeout(function() {
                            BatchRequestManager.prioritize(info.cardId);
                            if (!SparkCache.has(info.cardId)) {
                                BatchRequestManager.request(info.cardId).catch(function() {
                                    // 忽略预加载错误
                                });
                            }
                        }, 150);
                    }
                }, true);
               
                document.addEventListener('mouseleave', function(e) {
                    var card = e.target.closest && e.target.closest('.card-deck-trans');
                    if (card && hoverTimer) {
                        clearTimeout(hoverTimer);
                        hoverTimer = null;
                    }
                }, true);
               
                document.addEventListener('keydown', function(e) {
                    if (e.key === 'Escape') {
                        var sparkModal = DOMManager.getSparkModalOverlay();
                        var mainModal = DOMManager.getModalOverlay();
                       
                        if (document.querySelector('.spark-enlarged-container')) {
                            DOMManager.closeEnlargedCard();
                        } else if (sparkModal && sparkModal.classList.contains('active')) {
                            DOMManager.closeSparkModal();
                        } else if (mainModal && mainModal.classList.contains('active')) {
                            DOMManager.closeModal();
                         }
                         }
                    } else {
                        sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div><div style="color: #fff;">暂无闪光卡牌</div>';
                     }
                     }
                })
                .catch(function(error) {
                    sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div><div style="color: #ff6b6b;">加载失败,请重试</div>';
                    console.error('Error loading spark cards:', error);
                 });
                 });
               
        }
                window.addEventListener('beforeunload', function() {
       
                    SparkCache.flush();
        cardModalClose.addEventListener('click', function() {
                });
            cardModal.classList.remove('active');
               
            sparkModal.classList.remove('active');
                document.addEventListener('visibilitychange', function() {
            document.body.style.overflow = '';
                    if (document.hidden) {
        });
                        SparkCache.flush();
       
                    }
        sparkModalClose.addEventListener('click', function() {
                 });
            sparkModal.classList.remove('active');
        });
       
        cardModal.addEventListener('click', function(e) {
            if (e.target === cardModal) {
                cardModal.classList.remove('active');
                sparkModal.classList.remove('active');
                 document.body.style.overflow = '';
             }
             }
           
         });
            return {
                init: init
            };
         })();
          
          
         // ==================== 初始化 ====================
         sparkModal.addEventListener('click', function(e) {
        SparkCache.cleanup();
            if (e.target === sparkModal) {
        DOMManager.createModals();
                sparkModal.classList.remove('active');
        EventManager.init();
            }
         DOMManager.initExistingCards();
         });
          
          
         var observer = new MutationObserver(function(mutations) {
         document.addEventListener('keydown', function(e) {
             var newNodes = [];
             if (e.key === 'Escape') {
           
                 if (sparkModal.classList.contains('active')) {
            mutations.forEach(function(mutation) {
                     sparkModal.classList.remove('active');
                 if (mutation.addedNodes.length > 0) {
                 } else if (cardModal.classList.contains('active')) {
                     for (var i = 0; i < mutation.addedNodes.length; i++) {
                     cardModal.classList.remove('active');
                        newNodes.push(mutation.addedNodes[i]);
                     document.body.style.overflow = '';
                    }
                 }
            });
           
            if (newNodes.length > 0) {
                if (window.requestIdleCallback) {
                    requestIdleCallback(function() {
                        DOMManager.processNewCards(newNodes);
                    }, { timeout: 500 });
                } else {
                     setTimeout(function() {
                        DOMManager.processNewCards(newNodes);
                     }, 100);
                 }
                 }
             }
             }
        });
       
        bindCardEvents();
       
        var observer = new MutationObserver(function() {
            bindCardEvents();
         });
         });
          
          
第1,091行: 第311行:
     }
     }
      
      
     // ==================== 启动 ====================
     if (document.readyState === 'loading') {
    function start() {
        document.addEventListener('DOMContentLoaded', init);
        if (document.readyState === 'loading') {
    } else {
            document.addEventListener('DOMContentLoaded', function() {
        init();
                waitForMw(initCardWidget);
            });
        } else {
            waitForMw(initCardWidget);
        }
     }
     }
   
    start();
})();
})();
</script>
</script>
</includeonly>
</includeonly>

2026年1月17日 (六) 17:01的最新版本

此Widget用于为卡牌添加交互功能。 使用方法:在页面底部添加

×
×