微件:Card:修订间差异

来自卡厄思梦境WIKI
跳转到导航 跳转到搜索
律Rhyme留言 | 贡献
功能测试
律Rhyme留言 | 贡献
功能测试
 
(未显示同一用户的5个中间版本)
第1行: 第1行:
<includeonly>
<noinclude>
此Widget用于为卡牌添加交互功能。
使用方法:在页面底部添加 {{#widget:Card}}
[[分类:Widget]]
</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;
第30行: 第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;
    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;
     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-loading-progress {
    color: #aaa;
    font-size: 14px;
    margin-top: 10px;
}
/* 空列表提示 */
.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;
第276行: 第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>
第356行: 第176行:
     'use strict';
     'use strict';
      
      
     // ==================== 缓存管理器(优化版) ====================
     function init() {
    var SparkCache = (function() {
        var cardModal = document.getElementById('card-modal-overlay');
         var CACHE_KEY = 'spark_card_cache_v2';
        var cardModalContent = document.getElementById('card-modal-content');
         var CACHE_EXPIRE = 24 * 60 * 60 * 1000; // 24小时
        var cardModalClose = document.getElementById('card-modal-close');
        var sparkModal = document.getElementById('spark-modal-overlay');
         var sparkModalContent = document.getElementById('spark-modal-content');
         var sparkModalClose = document.getElementById('spark-modal-close');
          
          
        // 内存缓存层
         function bindCardEvents() {
        var memoryCache = null;
             var cards = document.querySelectorAll('.card-deck-trans');
        var isDirty = false;
        var saveTimer = null;
       
        // 从 localStorage 加载到内存
         function loadFromStorage() {
             if (memoryCache !== null) return memoryCache;
              
              
             try {
             cards.forEach(function(card) {
                var data = localStorage.getItem(CACHE_KEY);
                 if (card.dataset.bound || card.closest('.spark-modal-content') || card.closest('.card-modal-content')) {
                memoryCache = data ? JSON.parse(data) : {};
                     return;
            } catch (e) {
                 memoryCache = {};
            }
            return memoryCache;
        }
       
        // 延迟批量写入 localStorage
        function scheduleSave() {
            if (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); // 1秒后批量写入
        }
       
        // 清理过期缓存
        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);
                 card.dataset.bound = 'true';
                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;
                 card.addEventListener('click', function(e) {
                if (Date.now() - item.timestamp > CACHE_EXPIRE) {
                     e.preventDefault();
                    delete cache[cardId];
                    e.stopPropagation();
                    isDirty = true;
                      
                    scheduleSave();
                     var cardDeck = this.querySelector('.card-deck');
                    return null;
                     if (!cardDeck) return;
                }
               
                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);
            },
           
            // 批量获取
            getMultiple: function(cardIds) {
                var cache = loadFromStorage();
                var now = Date.now();
                var result = {};
                var hasExpired = false;
               
                cardIds.forEach(function(cardId) {
                    var item = cache[cardId];
                    if (item) {
                        if (now - item.timestamp <= CACHE_EXPIRE) {
                            result[cardId] = item.html;
                        } else {
                            delete cache[cardId];
                            hasExpired = true;
                        }
                    }
                });
               
                if (hasExpired) {
                     isDirty = true;
                     scheduleSave();
                }
               
                return result;
            },
           
            // 批量设置
            setMultiple: function(dataMap) {
                var cache = loadFromStorage();
                var now = Date.now();
               
                for (var cardId in dataMap) {
                    if (dataMap.hasOwnProperty(cardId)) {
                        cache[cardId] = {
                            html: dataMap[cardId],
                            timestamp: now
                        };
                    }
                }
               
                isDirty = true;
                scheduleSave();
            },
           
            cleanup: cleanup,
           
            // 页面卸载时立即保存
            flush: function() {
                if (saveTimer) {
                    clearTimeout(saveTimer);
                    saveTimer = null;
                }
                if (isDirty && memoryCache) {
                    try {
                        localStorage.setItem(CACHE_KEY, JSON.stringify(memoryCache));
                        isDirty = false;
                    } catch (e) {}
                }
            }
        };
    })();
   
    // ==================== 批量API请求管理器 ====================
    var BatchRequestManager = (function() {
        var pendingRequests = {};  // cardId -> [resolve, reject][]
        var requestQueue = [];
        var isProcessing = false;
        var batchSize = 5;  // 每批并行请求数
        var batchDelay = 50;  // 批次间延迟
       
        function processBatch() {
            if (isProcessing || requestQueue.length === 0) return;
           
            isProcessing = true;
           
            // 取出一批
            var batch = requestQueue.splice(0, batchSize);
            var promises = batch.map(function(cardId) {
                return fetchSparkList(cardId);
            });
           
            Promise.all(promises).then(function() {
                isProcessing = false;
                if (requestQueue.length > 0) {
                    setTimeout(processBatch, batchDelay);
                }
            });
        }
       
        function fetchSparkList(cardId) {
            return new Promise(function(resolve) {
                var api = new mw.Api();
                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 clonedCard = document.createElement('div');
                     var callbacks = pendingRequests[cardId] || [];
                     clonedCard.className = 'card-modal-card';
                     delete pendingRequests[cardId];
                     clonedCard.innerHTML = '<div class="card-deck">' + cardDeck.innerHTML + '</div>';
                     callbacks.forEach(function(cb) {
                        cb.resolve(html);
                    });
                      
                      
                     resolve();
                     cardModalContent.innerHTML = '';
                }).fail(function(error) {
                     cardModalContent.appendChild(clonedCard);
                    var callbacks = pendingRequests[cardId] || [];
                     delete pendingRequests[cardId];
                    callbacks.forEach(function(cb) {
                        cb.reject(error);
                    });
                      
                      
                     resolve();
                     var sparkEnable = this.dataset.sparkEnable;
                });
                     var sort = this.dataset.sort;
            });
                     var cardId = this.dataset.cardId;
        }
       
        return {
            // 请求单个卡牌(会自动合并到批量请求)
            request: function(cardId) {
                return new Promise(function(resolve, reject) {
                    // 先检查缓存
                     var cached = SparkCache.get(cardId);
                     if (cached !== null) {
                        resolve(cached);
                        return;
                    }
                      
                      
                    // 检查是否已在请求中
                     if (sparkEnable === '1' && sort !== '中立卡牌' && sort !== '怪物卡牌' && cardId) {
                     if (pendingRequests[cardId]) {
                         var sparkButton = document.createElement('button');
                         pendingRequests[cardId].push({ resolve: resolve, reject: reject });
                        sparkButton.className = 'card-spark-button';
                         return;
                        sparkButton.textContent = '闪光';
                        sparkButton.addEventListener('click', function(e) {
                            e.stopPropagation();
                            openSparkModal(cardId);
                        });
                         cardModalContent.appendChild(sparkButton);
                     }
                     }
                      
                      
                     // 新请求
                     cardModal.classList.add('active');
                    pendingRequests[cardId] = [{ resolve: resolve, reject: reject }];
                     document.body.style.overflow = 'hidden';
                    requestQueue.push(cardId);
                      
                    // 启动处理
                    if (!isProcessing) {
                        setTimeout(processBatch, 10);
                    }
                });
            },
           
            // 预加载多个卡牌(低优先级)
            preload: function(cardIds) {
                // 过滤掉已缓存和已在队列中的
                var toLoad = cardIds.filter(function(cardId) {
                    return !SparkCache.has(cardId) &&
                          !pendingRequests[cardId] &&
                          requestQueue.indexOf(cardId) === -1;
                });
               
                if (toLoad.length === 0) return;
               
                // 添加到队列末尾
                toLoad.forEach(function(cardId) {
                    pendingRequests[cardId] = [];
                    requestQueue.push(cardId);
                 });
                 });
               
                // 启动处理
                if (!isProcessing) {
                    setTimeout(processBatch, 100);
                }
            },
           
            // 提升优先级
            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();  // 使用 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);
           
            // 如果启用了闪光功能
            if (sparkEnable && sparkEnable.trim() !== '') {
                var sparkButton = document.createElement('button');
                sparkButton.className = 'spark-button';
                sparkButton.textContent = '闪光';
                sparkButton.dataset.cardId = cardId;
               
                if (SparkCache.has(cardId)) {
                    sparkButton.classList.add('cached');
                } else {
                    BatchRequestManager.prioritize(cardId);
                }
               
                modalContent.appendChild(sparkButton);
            }
           
            modalOverlay.classList.add('active');
            document.body.classList.add('card-modal-open');
           
            requestAnimationFrame(function() {
                modalOverlay.classList.add('fade-in');
             });
             });
         }
         }
          
          
        // 打开闪光卡牌模态框
         function openSparkModal(cardId) {
         function openSparkModal(cardId) {
             createModals();
             sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div><div class="spark-loading">加载中...</div>';
            sparkModal.classList.add('active');
              
              
             var cachedHtml = SparkCache.get(cardId);
             var apiUrl = mw.config.get('wgScriptPath') + '/api.php';
            var params = new URLSearchParams({
                action: 'parse',
                text: '{{Card/display/spark|' + cardId + '}}',
                prop: 'text',
                format: 'json',
                contentmodel: 'wikitext'
            });
              
              
             if (cachedHtml) {
             fetch(apiUrl + '?' + params.toString())
                showSparkContent(cachedHtml, true);
                 .then(function(response) {
            } else {
                     return response.json();
                sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-loading">加载中</div>';
                 })
               
                 .then(function(data) {
                sparkModalOverlay.classList.add('active');
                     if (data.parse && data.parse.text) {
                 requestAnimationFrame(function() {
                        var html = data.parse.text['*'];
                     sparkModalOverlay.classList.add('fade-in');
                        if (html.trim() === '' || html.indexOf('card-deck-trans') === -1) {
                 });
                            sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div><div style="color: #fff;">暂无闪光卡牌</div>';
                  
                        } else {
                BatchRequestManager.request(cardId).then(function(html) {
                            sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌</div>' + 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);
                   
                    // 收集需要预加载的卡牌ID
                    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);
                         }
                         }
                    } 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);
                 });
                 });
            });
           
            // 延迟预加载
            if (sparkCardIds.length > 0) {
                setTimeout(function() {
                    BatchRequestManager.preload(sparkCardIds);
                }, 2000);
            }
         }
         }
          
          
         // 初始化页面上已有的卡牌
         cardModalClose.addEventListener('click', function() {
        function initExistingCards() {
             cardModal.classList.remove('active');
             var cards = document.querySelectorAll('.card-deck-trans');
             sparkModal.classList.remove('active');
             var nodeArray = [];
             document.body.style.overflow = '';
            cards.forEach(function(card) {
        });
                nodeArray.push(card);
             });
            processNewCards(nodeArray);
        }
          
          
         return {
         sparkModalClose.addEventListener('click', function() {
            createModals: createModals,
             sparkModal.classList.remove('active');
            closeModal: closeModal,
         });
            closeSparkModal: closeSparkModal,
            closeEnlargedCard: closeEnlargedCard,
            openModal: openModal,
            openSparkModal: openSparkModal,
            showEnlargedCard: showEnlargedCard,
            processNewCards: processNewCards,
            initExistingCards: initExistingCards,
            getModalOverlay: function() { return modalOverlay; },
             getSparkModalOverlay: function() { return sparkModalOverlay; }
         };
    })();
   
    // ==================== 事件委托管理器 ====================
    var EventManager = (function() {
        var hoverTimer = null;
          
          
         // 获取卡牌信息
         cardModal.addEventListener('click', function(e) {
        function getCardInfo(cardElement) {
             if (e.target === cardModal) {
             var wrapper = cardElement.closest('.card-wrapper');
                cardModal.classList.remove('active');
            return {
                 sparkModal.classList.remove('active');
                 cardId: wrapper ? wrapper.dataset.cardId : '',
                 document.body.style.overflow = '';
                 sparkEnable: cardElement.dataset.sparkEnable || ''
             }
             };
         });
         }
          
          
         // 检查是否是有效的卡牌点击
         sparkModal.addEventListener('click', function(e) {
        function isValidCardClick(target) {
             if (e.target === sparkModal) {
            var card = target.closest('.card-deck-trans');
                sparkModal.classList.remove('active');
             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;
        }
          
          
         // 初始化事件监听
         document.addEventListener('keydown', function(e) {
        function init() {
            if (e.key === 'Escape') {
            // 使用事件委托处理所有点击事件
                 if (sparkModal.classList.contains('active')) {
            document.addEventListener('click', function(e) {
                     sparkModal.classList.remove('active');
                var target = e.target;
                } else if (cardModal.classList.contains('active')) {
               
                     cardModal.classList.remove('active');
                // 关闭按钮
                     document.body.style.overflow = '';
                 if (target.classList.contains('card-modal-close')) {
                     e.stopPropagation();
                     DOMManager.closeModal();
                     return;
                 }
                 }
               
             }
                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 {
                        // 普通卡牌 - 打开模态框
                        DOMManager.openModal(card, info.cardId, info.sparkEnable);
                    }
                }
             }, 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);
                        }
                    }, 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);
           
            // ESC键关闭
            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();
                    }
                }
            });
           
            // 页面卸载时保存缓存
            window.addEventListener('beforeunload', function() {
                SparkCache.flush();
            });
           
            // 页面可见性变化
            document.addEventListener('visibilitychange', function() {
                if (document.hidden) {
                    SparkCache.flush();
                }
            });
        }
          
          
         return {
         bindCardEvents();
            init: init
        };
    })();
   
    // ==================== 初始化 ====================
    function init() {
        // 清理过期缓存
        SparkCache.cleanup();
          
          
        // 创建模态框
         var observer = new MutationObserver(function() {
        DOMManager.createModals();
             bindCardEvents();
       
        // 初始化事件监听(事件委托)
        EventManager.init();
       
        // 处理已有卡牌
        DOMManager.initExistingCards();
       
        // 监听DOM变化 - 只处理新添加的节点
         var observer = new MutationObserver(function(mutations) {
             var newNodes = [];
           
            mutations.forEach(function(mutation) {
                if (mutation.addedNodes.length > 0) {
                    for (var i = 0; i < mutation.addedNodes.length; i++) {
                        newNodes.push(mutation.addedNodes[i]);
                    }
                }
            });
           
            if (newNodes.length > 0) {
                // 使用 requestIdleCallback 或 setTimeout 延迟处理
                if (window.requestIdleCallback) {
                    requestIdleCallback(function() {
                        DOMManager.processNewCards(newNodes);
                    }, { timeout: 500 });
                } else {
                    setTimeout(function() {
                        DOMManager.processNewCards(newNodes);
                    }, 100);
                }
            }
         });
         });
          
          
第1,098行: 第311行:
     }
     }
      
      
    // 启动
     if (document.readyState === 'loading') {
     if (document.readyState === 'loading') {
         document.addEventListener('DOMContentLoaded', init);
         document.addEventListener('DOMContentLoaded', init);

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

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

×
×