MediaWiki:Card.js
来自卡厄思梦境WIKI
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-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;
})();