MediaWiki

Card.js:修订间差异

来自卡厄思梦境WIKI

律Rhyme留言 | 贡献
无编辑摘要
律Rhyme留言 | 贡献
无编辑摘要
第1行: 第1行:
// MediaWiki:Card.js
/**
* MediaWiki 卡牌管理器
* 用于管理模块:卡牌/角色名的数据
*/
 
(function() {
(function() {
     'use strict';
     'use strict';
   
    // 检查是否在正确的页面
    if (mw.config.get('wgPageName') !== 'MediaWiki:Card') {
        return;
    }


     var api = new mw.Api();
     const CardManager = {
    var cardData = {};
        cards: [],
    var currentCharacter = null;
        currentCard: null,
 
        currentVariantIndex: null,
    // 获取所有"模块:卡牌/角色名"页面
        characterName: '',
    function loadAllCardModules() {
       
        api.get({
        // 初始化
             action: 'query',
        init: function() {
             list: 'allpages',
            this.characterName = this.getCharacterName();
             apprefix: '卡牌/',
            this.bindEvents();
             apnamespace: 828, // Module namespace
            this.loadCardModule();
             aplimit: 'max'
        },
        }).done(function(data) {
       
             var pages = data.query.allpages;
        // 从页面标题获取角色名
             var select = $('#character-select');
        getCharacterName: function() {
             select.empty();
            const match = mw.config.get('wgPageName').match(/模块:卡牌\/(.+)/);
             select.append('<option value="">选择角色</option>');
            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);
            });
              
              
             pages.forEach(function(page) {
             // 卡组类型变化
                var characterName = page.title.replace('模块:卡牌/', '');
            $('#deck-type').on('change', (e) => {
                 select.append($('<option>').val(characterName).text(characterName));
                 const isVariant = $(e.target).val() === '灵光一闪';
                this.toggleVariantFields(isVariant);
             });
             });
         });
         },
    }
       
 
        // 切换变体字段状态
    // 加载指定角色的卡牌数据
        toggleVariantFields: function(isVariant) {
    function loadCharacterCards(characterName) {
            $('#card-name').prop('disabled', isVariant);
        api.get({
             $('#card-art').prop('disabled', isVariant);
             action: 'query',
             $('#card-attr').prop('disabled', isVariant);
             prop: 'revisions',
             $('#card-rarity').prop('disabled', isVariant);
             titles: '模块:卡牌/' + characterName,
             $('#card-derived').prop('disabled', isVariant);
             rvprop: 'content',
        },
             rvslots: 'main'
       
        }).done(function(data) {
        // 插入格式
             var pages = data.query.pages;
        insertFormat: function(type, color) {
             for (var pageId in pages) {
             const textarea = document.getElementById('card-desc');
                if (pages[pageId].revisions) {
            const start = textarea.selectionStart;
                    var content = pages[pageId].revisions[0].slots.main['*'];
            const end = textarea.selectionEnd;
                    parseModuleContent(content, characterName);
            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);
             }
             }
        });
    }
    // 解析模块内容
    function parseModuleContent(content, characterName) {
        try {
            // 提取card表和cardOrder数组
            var cardMatch = content.match(/local\s+card\s*=\s*\{[\s\S]*?\n\}/);
            var orderMatch = content.match(/local\s+cardOrder\s*=\s*\{[\s\S]*?\n\}/);
              
              
             if (cardMatch && orderMatch) {
            const mechanism = $('#card-mechanism').val().trim();
                // 转换Lua表格式为JavaScript对象
             if (mechanism) variant['机制'] = mechanism;
                var cardDataStr = cardMatch[0]
           
                    .replace(/local\s+card\s*=/, 'var cardData =')
            const cardType = $('#card-type').val().trim();
                    .replace(/\["([^"]+)"\]/g, '"$1"')
            if (cardType) variant['类型'] = cardType;
                    .replace(/\[(\d+)\]/g, '$1')
           
                    .replace(/nil/g, 'null');
            const desc = $('#card-desc').val().trim();
                   
            if (desc) variant['描述'] = desc;
                 var orderDataStr = orderMatch[0]
           
                    .replace(/local\s+cardOrder\s*=/, 'var cardOrder =')
            const derived = $('#card-derived').val().trim();
                    .replace(/"/g, '"');
            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['卡组'] === '灵光一闪';
                  
                  
                 eval(cardDataStr);
                 this.toggleVariantFields(isVariant);
                eval(orderDataStr);
                  
                  
                 currentCharacter = characterName;
                 $('#card-art').val(variant.art || '');
                 displayCards(cardData, cardOrder);
                $('#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['衍生卡牌'] || '');
             }
             }
         } catch (e) {
         },
             console.error('解析失败:', e);
       
             mw.notify('解析模块内容失败', { type: 'error' });
        // 清空表单
        }
        clearForm: function() {
    }
             $('#card-name').val('');
 
             $('#card-art').val('');
    // 显示卡牌列表
            $('#deck-type').val('');
    function displayCards(cards, order) {
            $('#card-attr').val('');
        var container = $('#cards-container');
            $('#card-rarity').val('');
        container.empty();
            $('#card-ap').val('');
            $('#card-mechanism').val('');
            $('#card-type').val('');
            $('#card-desc').val('');
            $('#card-derived').val('');
           
            this.toggleVariantFields(false);
            this.currentCard = null;
            this.currentVariantIndex = null;
        },
          
          
         // 按照顺序显示卡牌
         // 添加卡牌
         (order || Object.keys(cards)).forEach(function(cardName) {
         addCard: function() {
             if (cards[cardName]) {
            const cardData = this.getFormData();
                 var cardDiv = createCardElement(cardName, cards[cardName]);
             if (!cardData.name) {
                 container.append(cardDiv);
                 mw.notify('卡牌名称不能为空!', {type: 'error'});
                 return;
             }
             }
        });
           
    }
            this.cards.push(cardData);
 
            this.currentCard = cardData;
    // 创建卡牌元素
            this.currentVariantIndex = 0;
    function createCardElement(cardName, variants) {
           
        var cardDiv = $('<div>').addClass('card-item');
            this.updateCardList();
        var header = $('<h3>').text(cardName).click(function() {
            this.updateVariantList();
             $(this).next('.card-variants').toggle();
            this.updatePreview();
         });
            this.clearForm();
        cardDiv.append(header);
           
             mw.notify(`卡牌 "${cardData.name}" 添加成功!`, {type: 'success'});
         },
          
          
         var variantsDiv = $('<div>').addClass('card-variants');
         // 添加变体
        variants.forEach(function(variant, index) {
        addVariant: function() {
             var variantDiv = createVariantElement(cardName, variant, index);
            if (!this.currentCard) {
             variantsDiv.append(variantDiv);
                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'});
        },
          
          
         // 添加新变体按钮
         // 保存卡牌
         var addButton = $('<button>').text('添加变体').click(function() {
         saveCard: function() {
             addCardVariant(cardName);
             if (!this.currentCard || this.currentVariantIndex === null) {
        });
                mw.notify('请先选择要保存的卡牌或变体!', {type: 'error'});
        variantsDiv.append(addButton);
                return;
       
             }
        cardDiv.append(variantsDiv);
              
        return cardDiv;
             const cardData = this.getFormData();
    }
             const isVariant = this.currentVariantIndex > 0;
 
    // 创建变体元素
    function createVariantElement(cardName, variant, index) {
        var variantDiv = $('<div>').addClass('card-variant');
       
        var fields = [
            { key: 'art', label: '图片', type: 'text' },
            { key: '卡组', label: '卡组', type: 'select',
              options: ['起始卡牌', '独特卡牌', '衍生卡牌', '灵光一闪', '神光一闪'] },
            { key: '属性', label: '属性', type: 'select',
              options: ['热情', '秩序', '正义', '本能', '虚无'] },
             { key: '稀有度', label: '稀有度', type: 'select',
              options: ['白', '蓝', '橙', '彩'] },
             { key: 'AP', label: 'AP', type: 'number' },
            { key: '类型', label: '类型', type: 'select',
              options: ['攻击', '技能', '强化', '状态异常'] },
             { key: '机制', label: '机制', type: 'text' },
            { key: '描述', label: '描述', type: 'textarea' },
            { key: '衍生卡牌', label: '衍生卡牌', type: 'text' }
        ];
       
        fields.forEach(function(field) {
            var fieldDiv = $('<div>').addClass('field');
             var label = $('<label>').text(field.label + ': ');
            var input;
              
              
             if (field.type === 'select') {
             if (isVariant) {
                 input = $('<select>');
                 const variant = cardData.variants[0];
                 $('<option>').val('').text('--选择--').appendTo(input);
                 variant['卡组'] = '灵光一闪';
                 field.options.forEach(function(opt) {
                 this.currentCard.variants[this.currentVariantIndex] = variant;
                    $('<option>').val(opt).text(opt).appendTo(input);
                });
                input.val(variant[field.key] || '');
            } else if (field.type === 'textarea') {
                input = $('<textarea>').val(variant[field.key] || '');
             } else {
             } else {
                 input = $('<input>').attr('type', field.type).val(variant[field.key] || '');
                 this.currentCard.name = cardData.name;
                this.currentCard.variants[0] = cardData.variants[0];
             }
             }
              
              
             input.attr('data-card', cardName)
             this.updateCardList();
                .attr('data-index', index)
            this.updateVariantList();
                .attr('data-field', field.key);
            this.updatePreview();
              
              
             fieldDiv.append(label).append(input);
             mw.notify('数据保存成功!', {type: 'success'});
            variantDiv.append(fieldDiv);
         },
         });
          
          
         // 删除按钮
         // 删除卡牌
         var deleteButton = $('<button>').text('删除').click(function() {
         deleteCard: function() {
             deleteCardVariant(cardName, index);
            if (!this.currentCard) {
        });
                mw.notify('请先选择要删除的卡牌!', {type: 'error'});
        variantDiv.append(deleteButton);
                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'});
        },
          
          
         return variantDiv;
         // 删除变体
    }
        deleteVariant: function() {
 
            if (!this.currentCard || this.currentVariantIndex === null) {
    // 添加新卡牌
                mw.notify('请先选择要删除的变体!', {type: 'error'});
    function addNewCard() {
                return;
        var cardName = prompt('输入新卡牌名称:');
            }
        if (!cardName) 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'});
        },
          
          
         cardData[cardName] = [{
         // 选择卡牌
            art: '',
        selectCard: function(index) {
             '卡组': '',
             this.currentCard = this.cards[index];
             '属性': '',
             this.currentVariantIndex = 0;
             '稀有度': '',
             this.updateVariantList();
             'AP': 1,
             this.setFormData(this.currentCard, 0);
            '类型': '',
         },
            '机制': '',
            '描述': '',
            '衍生卡牌': ''
         }];
          
          
         displayCards(cardData, Object.keys(cardData));
         // 选择变体
    }
        selectVariant: function(index) {
 
            if (!this.currentCard) return;
    // 添加卡牌变体
             this.currentVariantIndex = index;
    function addCardVariant(cardName) {
            this.setFormData(this.currentCard, index);
        if (!cardData[cardName]) {
         },
             cardData[cardName] = [];
         }
          
          
         cardData[cardName].push({
         // 更新卡牌列表
             art: '',
        updateCardList: function() {
             '卡组': '',
             const $list = $('#card-list');
             '属性': '',
             $list.empty();
             '稀有度': '',
              
            'AP': 1,
             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);
             });
         },
          
          
         displayCards(cardData, Object.keys(cardData));
         // 更新变体列表
    }
        updateVariantList: function() {
 
            const $list = $('#variant-list');
    // 删除卡牌变体
            $list.empty();
    function deleteCardVariant(cardName, index) {
           
        if (confirm('确认删除这个变体?')) {
            if (this.currentCard) {
            cardData[cardName].splice(index, 1);
                this.currentCard.variants.forEach((variant, index) => {
            if (cardData[cardName].length === 0) {
                    const deck = variant['卡组'] || '未知';
                 delete cardData[cardName];
                    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);
                 });
             }
             }
            displayCards(cardData, Object.keys(cardData));
         },
         }
    }
 
    // 保存数据
    function saveCardData() {
        if (!currentCharacter) {
            mw.notify('请先选择角色', { type: 'warning' });
            return;
        }
          
          
         // 收集表单数据更新cardData
         // 生成Lua代码
         $('#cards-container input, #cards-container select, #cards-container textarea').each(function() {
         generateLuaCode: function() {
             var $this = $(this);
             if (this.cards.length === 0) {
            var cardName = $this.data('card');
                return '-- 暂无数据';
             var index = $this.data('index');
             }
             var field = $this.data('field');
           
            var value = $this.val();
             let lua = 'local p = {}\n\n';
              
              
             if (cardData[cardName] && cardData[cardName][index]) {
             // cardOrder
                if (field === 'AP') {
            lua += 'local cardOrder = {\n';
                     value = parseInt(value) || 0;
            this.cards.forEach(card => {
                }
                if (card.variants[0] && card.variants[0]['卡组'] !== '衍生卡牌') {
                if (value) {
                     lua += `    "${this.escapeLua(card.name)}",\n`;
                    cardData[cardName][index][field] = value;
                } else {
                    delete cardData[cardName][index][field];
                 }
                 }
            });
            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'});
            });
        },
          
          
         var moduleContent = generateModuleContent(cardData);
         // 从模块导入
        importFromModule: function() {
            const moduleName = prompt('请输入要导入的模块名称(如:妮雅):');
            if (!moduleName) return;
           
            this.loadCardModule(moduleName);
        },
          
          
         api.postWithToken('csrf', {
         // 加载卡牌模块
             action: 'edit',
        loadCardModule: function(characterName) {
            title: '模块:卡牌/' + currentCharacter,
            const name = characterName || this.characterName;
            text: moduleContent,
            if (!name) return;
            summary: '通过卡牌管理器更新'
           
        }).done(function() {
            const moduleName = `模块:卡牌/${name}`;
            mw.notify('保存成功!', { type: 'success' });
              
        }).fail(function(code, data) {
            new mw.Api().get({
            mw.notify('保存失败: ' + (data.error ? data.error.info : code), { type: 'error' });
                action: 'query',
        });
                prop: 'revisions',
    }
                titles: moduleName,
 
                rvprop: 'content',
    // 生成模块内容
                rvslots: 'main'
    function generateModuleContent(data) {
            }).done((data) => {
         var content = 'local p = {}\n\n';
                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'});
            });
         },
          
          
         // 生成cardOrder
         // 解析Lua代码
         content += 'local cardOrder = {\n';
         parseLuaCode: function(content) {
        Object.keys(data).forEach(function(cardName) {
            try {
            content += '   "' + cardName + '",\n';
                this.cards = [];
        });
               
        content += '}\n\n';
                // 提取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'});
            }
        },
          
          
         // 生成card表
         // 提取卡牌变体
         content += 'local card = {\n';
         extractCardVariants: function(content, cardName) {
        Object.keys(data).forEach(function(cardName) {
            const variants = [];
             content += '    ["' + cardName + '"] = {\n';
            const escapedName = cardName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
             const pattern = new RegExp(`\\["${escapedName}"\\]\\s*=\\s*\\{`, 'g');
            const match = pattern.exec(content);
              
              
             data[cardName].forEach(function(variant) {
             if (!match) return variants;
                 content += '        {\n';
           
                 Object.keys(variant).forEach(function(key) {
            let pos = match.index + match[0].length;
                     var value = variant[key];
            let braceCount = 1;
                     if (value !== null && value !== undefined && value !== '') {
            let variantStart = -1;
                        if (key === 'AP' || !isNaN(value)) {
            let inString = false;
                             content += '            ["' + key + '"] = ' + value + ',\n';
            let escape = false;
                         } else {
           
                             content += '            ["' + key + '"] = "' + value.replace(/"/g, '\\"') + '",\n';
            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;
                         }
                         }
                     }
                     }
                 });
                 }
                 content += '        },\n';
                  
             });
                pos++;
             }
              
              
             content += '    },\n';
             return variants;
         });
         },
        content += '}\n\n';
          
          
         content += 'p.card = card\n';
         // 解析变体
        content += 'p.cardOrder = cardOrder\n\n';
        parseVariant: function(content) {
        content += 'return p\n';
            const variant = {};
       
            const fieldPattern = /\["([^"]+)"\]\s*=\s*("([^"\\]*(\\.[^"\\]*)*)"|(-?\d+|[XxA-Za-z]+))/g;
        return content;
            let match;
    }
           
 
            while ((match = fieldPattern.exec(content)) !== null) {
    // 初始化页面
                const fieldName = match[1];
    $(function() {
                let value;
        loadAllCardModules();
               
       
                if (match[2].startsWith('"')) {
        // 绑定事件
                    // 字符串值
        $('#character-select').on('change', function() {
                    value = match[3]
            var characterName = $(this).val();
                        .replace(/\\"/g, '"')
            if (characterName) {
                        .replace(/\\\\/g, '\\')
                 loadCharacterCards(characterName);
                        .replace(/\\n/g, '\n');
                } else {
                    // 数字或其他值
                    const val = match[5];
                    if (/^-?\d+$/.test(val)) {
                        value = parseInt(val);
                    } else {
                        value = val;
                    }
                 }
               
                variant[fieldName] = value;
             }
             }
         });
           
       
            return variant;
        $('#add-card-btn').click(addNewCard);
         }
         $('#save-btn').click(saveCardData);
    };
        $('#refresh-btn').click(loadAllCardModules);
   
    // 页面加载完成后初始化
    $(document).ready(() => {
         if (mw.config.get('wgPageName') === 'MediaWiki:Card') {
            CardManager.init();
        }
     });
     });
 
   
    window.CardManager = CardManager;
})();
})();

2025年10月2日 (四) 10:14的版本

/**
 * 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;
})();