MediaWiki

MediaWiki:Card.js

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献2025年10月2日 (四) 10:14的版本

注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。

  • Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5Ctrl-R(Mac为⌘-R
  • Google Chrome:Ctrl-Shift-R(Mac为⌘-Shift-R
  • Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
/**
 * MediaWiki 卡牌管理器
 * 用于管理模块:卡牌/角色名的数据
 */

(function() {
    'use strict';

    const CardManager = {
        cards: [],
        currentCard: null,
        currentVariantIndex: null,
        characterName: '',
        
        // 初始化
        init: function() {
            this.characterName = this.getCharacterName();
            this.bindEvents();
            this.loadCardModule();
        },
        
        // 从页面标题获取角色名
        getCharacterName: function() {
            const match = mw.config.get('wgPageName').match(/模块:卡牌\/(.+)/);
            return match ? match[1] : '';
        },
        
        // 绑定事件
        bindEvents: function() {
            $('#add-card-btn').on('click', () => this.addCard());
            $('#add-variant-btn').on('click', () => this.addVariant());
            $('#save-card-btn').on('click', () => this.saveCard());
            $('#clear-form-btn').on('click', () => this.clearForm());
            $('#delete-card-btn').on('click', () => this.deleteCard());
            $('#delete-variant-btn').on('click', () => this.deleteVariant());
            $('#export-btn').on('click', () => this.exportToModule());
            $('#import-btn').on('click', () => this.importFromModule());
            
            // 格式化按钮
            $('#blue-text-btn').on('click', () => this.insertFormat('文本', '蓝'));
            $('#green-text-btn').on('click', () => this.insertFormat('文本', '绿'));
            $('#green-stroke-btn').on('click', () => this.insertFormat('描边', '绿'));
            $('#br-btn').on('click', () => this.insertBr());
            
            // 卡牌选择
            $('#card-list').on('click', '.card-item', (e) => {
                const index = $(e.currentTarget).data('index');
                this.selectCard(index);
            });
            
            // 变体选择
            $('#variant-list').on('click', '.variant-item', (e) => {
                const index = $(e.currentTarget).data('index');
                this.selectVariant(index);
            });
            
            // 卡组类型变化
            $('#deck-type').on('change', (e) => {
                const isVariant = $(e.target).val() === '灵光一闪';
                this.toggleVariantFields(isVariant);
            });
        },
        
        // 切换变体字段状态
        toggleVariantFields: function(isVariant) {
            $('#card-name').prop('disabled', isVariant);
            $('#card-art').prop('disabled', isVariant);
            $('#card-attr').prop('disabled', isVariant);
            $('#card-rarity').prop('disabled', isVariant);
            $('#card-derived').prop('disabled', isVariant);
        },
        
        // 插入格式
        insertFormat: function(type, color) {
            const textarea = document.getElementById('card-desc');
            const start = textarea.selectionStart;
            const end = textarea.selectionEnd;
            const text = textarea.value;
            const selectedText = text.substring(start, end);
            
            const formatted = `{{${type}|${color}|${selectedText}}}`;
            textarea.value = text.substring(0, start) + formatted + text.substring(end);
            textarea.focus();
            textarea.setSelectionRange(start + formatted.length - selectedText.length - 2, start + formatted.length - 2);
        },
        
        // 插入换行
        insertBr: function() {
            const textarea = document.getElementById('card-desc');
            const start = textarea.selectionStart;
            const text = textarea.value;
            textarea.value = text.substring(0, start) + '<br>' + text.substring(start);
            textarea.focus();
            textarea.setSelectionRange(start + 4, start + 4);
        },
        
        // 获取表单数据
        getFormData: function() {
            const variant = {};
            
            const art = $('#card-art').val().trim();
            if (art) variant.art = art;
            
            const deck = $('#deck-type').val().trim();
            if (deck) variant['卡组'] = deck;
            
            const attr = $('#card-attr').val().trim();
            if (attr) variant['属性'] = attr;
            
            const rarity = $('#card-rarity').val().trim();
            if (rarity) variant['稀有度'] = rarity;
            
            const ap = $('#card-ap').val().trim();
            if (ap) {
                variant['AP'] = ap.toUpperCase() === 'X' ? 'X' : parseInt(ap);
            }
            
            const mechanism = $('#card-mechanism').val().trim();
            if (mechanism) variant['机制'] = mechanism;
            
            const cardType = $('#card-type').val().trim();
            if (cardType) variant['类型'] = cardType;
            
            const desc = $('#card-desc').val().trim();
            if (desc) variant['描述'] = desc;
            
            const derived = $('#card-derived').val().trim();
            if (derived) variant['衍生卡牌'] = derived;
            
            return {
                name: $('#card-name').val().trim(),
                variants: [variant]
            };
        },
        
        // 设置表单数据
        setFormData: function(card, variantIndex = 0) {
            $('#card-name').val(card.name);
            
            if (variantIndex < card.variants.length) {
                const variant = card.variants[variantIndex];
                const isVariant = variant['卡组'] === '灵光一闪';
                
                this.toggleVariantFields(isVariant);
                
                $('#card-art').val(variant.art || '');
                $('#deck-type').val(variant['卡组'] || '');
                $('#card-attr').val(variant['属性'] || '');
                $('#card-rarity').val(variant['稀有度'] || '');
                $('#card-ap').val(variant['AP'] !== undefined ? variant['AP'] : '');
                $('#card-mechanism').val(variant['机制'] || '');
                $('#card-type').val(variant['类型'] || '');
                $('#card-desc').val(variant['描述'] || '');
                $('#card-derived').val(variant['衍生卡牌'] || '');
            }
        },
        
        // 清空表单
        clearForm: function() {
            $('#card-name').val('');
            $('#card-art').val('');
            $('#deck-type').val('');
            $('#card-attr').val('');
            $('#card-rarity').val('');
            $('#card-ap').val('');
            $('#card-mechanism').val('');
            $('#card-type').val('');
            $('#card-desc').val('');
            $('#card-derived').val('');
            
            this.toggleVariantFields(false);
            this.currentCard = null;
            this.currentVariantIndex = null;
        },
        
        // 添加卡牌
        addCard: function() {
            const cardData = this.getFormData();
            if (!cardData.name) {
                mw.notify('卡牌名称不能为空!', {type: 'error'});
                return;
            }
            
            this.cards.push(cardData);
            this.currentCard = cardData;
            this.currentVariantIndex = 0;
            
            this.updateCardList();
            this.updateVariantList();
            this.updatePreview();
            this.clearForm();
            
            mw.notify(`卡牌 "${cardData.name}" 添加成功!`, {type: 'success'});
        },
        
        // 添加变体
        addVariant: function() {
            if (!this.currentCard) {
                mw.notify('请先选择一个卡牌!', {type: 'error'});
                return;
            }
            
            const variantData = this.getFormData();
            const variant = variantData.variants[0];
            variant['卡组'] = '灵光一闪';
            
            this.currentCard.variants.push(variant);
            this.currentVariantIndex = this.currentCard.variants.length - 1;
            
            this.updateVariantList();
            this.updatePreview();
            
            mw.notify(`为 "${this.currentCard.name}" 添加变体成功!`, {type: 'success'});
        },
        
        // 保存卡牌
        saveCard: function() {
            if (!this.currentCard || this.currentVariantIndex === null) {
                mw.notify('请先选择要保存的卡牌或变体!', {type: 'error'});
                return;
            }
            
            const cardData = this.getFormData();
            const isVariant = this.currentVariantIndex > 0;
            
            if (isVariant) {
                const variant = cardData.variants[0];
                variant['卡组'] = '灵光一闪';
                this.currentCard.variants[this.currentVariantIndex] = variant;
            } else {
                this.currentCard.name = cardData.name;
                this.currentCard.variants[0] = cardData.variants[0];
            }
            
            this.updateCardList();
            this.updateVariantList();
            this.updatePreview();
            
            mw.notify('数据保存成功!', {type: 'success'});
        },
        
        // 删除卡牌
        deleteCard: function() {
            if (!this.currentCard) {
                mw.notify('请先选择要删除的卡牌!', {type: 'error'});
                return;
            }
            
            if (!confirm(`确定要删除卡牌 "${this.currentCard.name}" 吗?`)) {
                return;
            }
            
            const index = this.cards.indexOf(this.currentCard);
            this.cards.splice(index, 1);
            
            this.currentCard = null;
            this.currentVariantIndex = null;
            
            this.updateCardList();
            this.updateVariantList();
            this.updatePreview();
            this.clearForm();
            
            mw.notify('卡牌删除成功!', {type: 'success'});
        },
        
        // 删除变体
        deleteVariant: function() {
            if (!this.currentCard || this.currentVariantIndex === null) {
                mw.notify('请先选择要删除的变体!', {type: 'error'});
                return;
            }
            
            if (this.currentVariantIndex === 0) {
                mw.notify('不能删除主卡牌变体!', {type: 'error'});
                return;
            }
            
            if (!confirm('确定要删除这个变体吗?')) {
                return;
            }
            
            this.currentCard.variants.splice(this.currentVariantIndex, 1);
            this.currentVariantIndex = 0;
            
            this.updateVariantList();
            this.updatePreview();
            this.setFormData(this.currentCard, 0);
            
            mw.notify('变体删除成功!', {type: 'success'});
        },
        
        // 选择卡牌
        selectCard: function(index) {
            this.currentCard = this.cards[index];
            this.currentVariantIndex = 0;
            this.updateVariantList();
            this.setFormData(this.currentCard, 0);
        },
        
        // 选择变体
        selectVariant: function(index) {
            if (!this.currentCard) return;
            this.currentVariantIndex = index;
            this.setFormData(this.currentCard, index);
        },
        
        // 更新卡牌列表
        updateCardList: function() {
            const $list = $('#card-list');
            $list.empty();
            
            this.cards.forEach((card, index) => {
                const $item = $('<div>')
                    .addClass('card-item')
                    .attr('data-index', index)
                    .text(card.name);
                
                if (this.currentCard === card) {
                    $item.addClass('active');
                }
                
                $list.append($item);
            });
        },
        
        // 更新变体列表
        updateVariantList: function() {
            const $list = $('#variant-list');
            $list.empty();
            
            if (this.currentCard) {
                this.currentCard.variants.forEach((variant, index) => {
                    const deck = variant['卡组'] || '未知';
                    const label = index === 0 ? `主卡牌 (${deck})` : `变体 ${index} (灵光一闪)`;
                    
                    const $item = $('<div>')
                        .addClass('variant-item')
                        .attr('data-index', index)
                        .text(label);
                    
                    if (this.currentVariantIndex === index) {
                        $item.addClass('active');
                    }
                    
                    $list.append($item);
                });
            }
        },
        
        // 生成Lua代码
        generateLuaCode: function() {
            if (this.cards.length === 0) {
                return '-- 暂无数据';
            }
            
            let lua = 'local p = {}\n\n';
            
            // cardOrder
            lua += 'local cardOrder = {\n';
            this.cards.forEach(card => {
                if (card.variants[0] && card.variants[0]['卡组'] !== '衍生卡牌') {
                    lua += `    "${this.escapeLua(card.name)}",\n`;
                }
            });
            lua += '}\n\n';
            
            // card data
            lua += 'local card = {\n';
            this.cards.forEach(card => {
                lua += `    ["${this.escapeLua(card.name)}"] = {\n`;
                
                card.variants.forEach(variant => {
                    lua += '        {\n';
                    
                    const fieldOrder = ['art', '卡组', '属性', '稀有度', 'AP', '机制', '类型', '描述', '衍生卡牌'];
                    
                    fieldOrder.forEach(field => {
                        if (variant[field] !== undefined && variant[field] !== '') {
                            const value = variant[field];
                            if (typeof value === 'string') {
                                lua += `            ["${field}"] = "${this.escapeLua(value)}",\n`;
                            } else {
                                lua += `            ["${field}"] = ${value},\n`;
                            }
                        }
                    });
                    
                    lua += '        },\n';
                });
                
                lua += '    },\n';
            });
            lua += '}\n\n';
            
            lua += 'p.card = card\n';
            lua += 'p.cardOrder = cardOrder\n\n';
            lua += 'return p\n';
            
            return lua;
        },
        
        // 转义Lua字符串
        escapeLua: function(str) {
            if (typeof str !== 'string') return str;
            return str.replace(/\\/g, '\\\\')
                      .replace(/"/g, '\\"')
                      .replace(/\n/g, '\\n');
        },
        
        // 更新预览
        updatePreview: function() {
            const code = this.generateLuaCode();
            $('#code-preview').text(code);
        },
        
        // 导出到模块
        exportToModule: function() {
            if (this.cards.length === 0) {
                mw.notify('没有数据可导出!', {type: 'error'});
                return;
            }
            
            const code = this.generateLuaCode();
            const moduleName = `模块:卡牌/${this.characterName}`;
            
            // 使用MediaWiki API保存
            new mw.Api().postWithToken('csrf', {
                action: 'edit',
                title: moduleName,
                text: code,
                summary: '更新卡牌数据',
                contentmodel: 'Scribunto'
            }).done(() => {
                mw.notify(`成功保存到 ${moduleName}`, {type: 'success'});
            }).fail((err) => {
                mw.notify('保存失败: ' + err, {type: 'error'});
            });
        },
        
        // 从模块导入
        importFromModule: function() {
            const moduleName = prompt('请输入要导入的模块名称(如:妮雅):');
            if (!moduleName) return;
            
            this.loadCardModule(moduleName);
        },
        
        // 加载卡牌模块
        loadCardModule: function(characterName) {
            const name = characterName || this.characterName;
            if (!name) return;
            
            const moduleName = `模块:卡牌/${name}`;
            
            new mw.Api().get({
                action: 'query',
                prop: 'revisions',
                titles: moduleName,
                rvprop: 'content',
                rvslots: 'main'
            }).done((data) => {
                const pages = data.query.pages;
                const page = pages[Object.keys(pages)[0]];
                
                if (page.revisions) {
                    const content = page.revisions[0].slots.main['*'];
                    this.parseLuaCode(content);
                    mw.notify(`成功从 ${moduleName} 导入数据`, {type: 'success'});
                } else {
                    mw.notify('模块不存在', {type: 'error'});
                }
            }).fail(() => {
                mw.notify('加载失败', {type: 'error'});
            });
        },
        
        // 解析Lua代码
        parseLuaCode: function(content) {
            try {
                this.cards = [];
                
                // 提取cardOrder
                const orderMatch = content.match(/local\s+cardOrder\s*=\s*\{([^}]+)\}/s);
                const cardOrder = [];
                if (orderMatch) {
                    const names = orderMatch[1].match(/"([^"]+)"/g);
                    if (names) {
                        names.forEach(name => {
                            cardOrder.push(name.replace(/"/g, ''));
                        });
                    }
                }
                
                // 提取card表
                const cardMatch = content.match(/local\s+card\s*=\s*\{(.+)\}\s*p\.card/s);
                if (!cardMatch) {
                    mw.notify('无法解析卡牌数据', {type: 'error'});
                    return;
                }
                
                const cardContent = cardMatch[1];
                
                // 解析每个卡牌
                const cardPattern = /\["([^"]+)"\]\s*=\s*\{/g;
                let match;
                const allCardNames = [];
                
                while ((match = cardPattern.exec(cardContent)) !== null) {
                    const cardName = match[1];
                    if (!allCardNames.includes(cardName)) {
                        allCardNames.push(cardName);
                    }
                }
                
                // 按cardOrder排序
                const sortedNames = [];
                cardOrder.forEach(name => {
                    if (allCardNames.includes(name)) {
                        sortedNames.push(name);
                        allCardNames.splice(allCardNames.indexOf(name), 1);
                    }
                });
                sortedNames.push(...allCardNames);
                
                // 解析每个卡牌的变体
                sortedNames.forEach(cardName => {
                    const variants = this.extractCardVariants(cardContent, cardName);
                    if (variants.length > 0) {
                        this.cards.push({
                            name: cardName,
                            variants: variants
                        });
                    }
                });
                
                this.updateCardList();
                this.updateVariantList();
                this.updatePreview();
                this.clearForm();
                
            } catch (e) {
                console.error('解析错误:', e);
                mw.notify('解析失败: ' + e.message, {type: 'error'});
            }
        },
        
        // 提取卡牌变体
        extractCardVariants: function(content, cardName) {
            const variants = [];
            const escapedName = cardName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
            const pattern = new RegExp(`\\["${escapedName}"\\]\\s*=\\s*\\{`, 'g');
            const match = pattern.exec(content);
            
            if (!match) return variants;
            
            let pos = match.index + match[0].length;
            let braceCount = 1;
            let variantStart = -1;
            let inString = false;
            let escape = false;
            
            while (pos < content.length && braceCount > 0) {
                const char = content[pos];
                
                if (escape) {
                    escape = false;
                    pos++;
                    continue;
                }
                
                if (char === '\\' && inString) {
                    escape = true;
                    pos++;
                    continue;
                }
                
                if (char === '"') {
                    inString = !inString;
                }
                
                if (!inString) {
                    if (char === '{') {
                        if (braceCount === 1 && variantStart === -1) {
                            variantStart = pos + 1;
                        }
                        braceCount++;
                    } else if (char === '}') {
                        braceCount--;
                        if (braceCount === 1 && variantStart !== -1) {
                            const variantContent = content.substring(variantStart, pos);
                            const variant = this.parseVariant(variantContent);
                            variants.push(variant);
                            variantStart = -1;
                        }
                    }
                }
                
                pos++;
            }
            
            return variants;
        },
        
        // 解析变体
        parseVariant: function(content) {
            const variant = {};
            const fieldPattern = /\["([^"]+)"\]\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|(-?\d+|[XxA-Za-z]+))/g;
            let match;
            
            while ((match = fieldPattern.exec(content)) !== null) {
                const fieldName = match[1];
                let value;
                
                if (match[2].startsWith('"')) {
                    // 字符串值
                    value = match[3]
                        .replace(/\\"/g, '"')
                        .replace(/\\\\/g, '\\')
                        .replace(/\\n/g, '\n');
                } else {
                    // 数字或其他值
                    const val = match[5];
                    if (/^-?\d+$/.test(val)) {
                        value = parseInt(val);
                    } else {
                        value = val;
                    }
                }
                
                variant[fieldName] = value;
            }
            
            return variant;
        }
    };
    
    // 页面加载完成后初始化
    $(document).ready(() => {
        if (mw.config.get('wgPageName') === 'MediaWiki:Card') {
            CardManager.init();
        }
    });
    
    window.CardManager = CardManager;
})();