Dantes (обсуждение | вклад) Нет описания правки Метка: отменено |
Dantes (обсуждение | вклад) Нет описания правки Метка: ручная отмена |
||
| (не показано 38 промежуточных версий этого же участника) | |||
| Строка 4: | Строка 4: | ||
local function getArgs(frame) | local function getArgs(frame) | ||
if type(frame) == "table" and frame.args then return frame.args end | |||
return mw.getCurrentFrame():getParent().args or {} | |||
end | end | ||
local function splitLines(s) | local function splitLines(s) | ||
local t = {} | |||
if not s then return t end | |||
for line in mw.text.gsplit(s, "\n") do | |||
line = mw.text.trim(line) | |||
if line ~= "" then table.insert(t, line) end | |||
end | |||
return t | |||
end | end | ||
local function parseLineToItem(l) | local function parseLineToItem(l) | ||
local name, qty = mw.ustring.match(l, "^(.*)%s*%[%s*([%d,%.]+)%s*%]%s*$") | |||
if not name then name = l; qty = "1" end | |||
return { name = mw.text.trim(name), qty = qty or "1" } | |||
end | end | ||
local function parseRecipeString(recipeStr) | local function parseRecipeString(recipeStr) | ||
if not recipeStr or recipeStr == "" then return {} end | |||
local lines = splitLines(recipeStr) | |||
local recipes = {} | |||
local currentInputs = {} | |||
local i = 1 | |||
while i <= #lines do | |||
local line = lines[i] | |||
local lower = mw.ustring.lower(line) | |||
if lower:find("смеш") then | |||
if #currentInputs > 0 then | |||
local outputs = {} | |||
i = i + 1 | |||
while i <= #lines do | |||
local nextLine = lines[i] | |||
local nextLower = mw.ustring.lower(nextLine) | |||
if nextLower:find("смеш") then break end | |||
table.insert(outputs, parseLineToItem(nextLine)) | |||
i = i + 1 | |||
end | |||
table.insert(recipes, { | |||
inputs = currentInputs, | |||
outputs = outputs, | |||
action = "Смешайте" | |||
}) | |||
currentInputs = {} | |||
else | |||
i = i + 1 | |||
end | |||
else | |||
table.insert(currentInputs, parseLineToItem(line)) | |||
i = i + 1 | |||
end | |||
end | |||
if #currentInputs > 0 then | |||
table.insert(recipes, { inputs = currentInputs, outputs = {}, action = "" }) | |||
end | end | ||
return recipes | |||
end | end | ||
local function buildRecipeHtml(rec, | local function buildRecipeHtml(rec, index) | ||
if not rec or (#rec.inputs == 0 and #rec.outputs == 0) then return nil end | |||
local container = html.create("div"):addClass("chem-recipe-block") | |||
if index == 1 then | |||
local heading = html.create("div"):addClass("chem-heading"):attr("data-kind", "recipe") | |||
local hcontent = heading:tag("div"):addClass("chem-heading-content") | |||
hcontent:tag("span"):addClass("heading-text"):wikitext("Рецепты") | |||
hcontent:tag("span"):addClass("collapse-btn"):wikitext("развернуть") | |||
container:node(heading) | |||
end | |||
local collapsible = html.create("div"):addClass("collapsible collapsed"):attr("data-kind", "recipe") | |||
local inner = html.create("div"):addClass("chem-recipe") | |||
-- INPUTS (левая колонка) | |||
local inputsDiv = html.create("div"):addClass("recipe-inputs") | |||
for _, it in ipairs(rec.inputs) do | |||
local item = inputsDiv:tag("div"):addClass("recipe-item") | |||
-- Используем MediaWiki-синтаксис файла — надёжно рендерится | |||
item:wikitext('[[File:Beaker.png|36px|class=chem-beaker]] ' .. it.name .. ' [' .. it.qty .. ']') | |||
end | |||
inner:node(inputsDiv) | |||
-- Действие | |||
if rec.action ~= "" then | |||
if | local actionDiv = inner:tag("div"):addClass("recipe-action") | ||
actionDiv:wikitext(rec.action) | |||
end | end | ||
-- OUTPUTS (правая колонка) | |||
local outputsDiv = html.create("div"):addClass("recipe-outputs") | |||
for _, it in ipairs(rec.outputs) do | |||
local item = outputsDiv:tag("div"):addClass("recipe-item") | |||
item:wikitext('[[File:Beaker.png|36px|class=chem-beaker]] ' .. it.name .. ' [' .. it.qty .. ']') | |||
end | |||
inner:node(outputsDiv) | |||
collapsible:node(inner) | |||
container:node(collapsible) | |||
return container | |||
end | end | ||
local function buildEffectsHtml(effects) | local function buildEffectsHtml(effects) | ||
if not effects or effects == "" or effects == "Нет" then return nil end | |||
local container = html.create("div"):addClass("chem-effects-block") | |||
local heading = html.create("div"):addClass("chem-heading"):attr("data-kind", "effects") | |||
local hcontent = heading:tag("div"):addClass("chem-heading-content") | |||
hcontent:tag("span"):addClass("heading-text"):wikitext("Эффекты") | |||
hcontent:tag("span"):addClass("collapse-btn"):wikitext("развернуть") | |||
container:node(heading) | |||
local collapsible = html.create("div"):addClass("collapsible collapsed"):attr("data-kind", "effects") | |||
local inner = html.create("div"):addClass("chem-effects") | |||
for effect in mw.text.gsplit(effects, "[;\n]") do | |||
effect = mw.text.trim(effect) | |||
if effect ~= "" then | |||
inner:tag("div"):addClass("chem-effect-line"):wikitext(effect) | |||
end | |||
end | |||
collapsible:node(inner) | |||
container:node(collapsible) | |||
return container | |||
end | end | ||
function p.card(frame) | function p.card(frame) | ||
local args = getArgs(frame) | |||
local name = args.name or "—" | |||
local color = args.color or "#8cf" | |||
local border = args.border or "#444" | |||
local desc = args.desc or "" | |||
local recipes = {} | |||
local i = 1 | |||
while true do | |||
local key = i == 1 and "recipe" or ("recipe" .. i) | |||
local recipeStr = args[key] | |||
if not recipeStr then break end | |||
local parsedList = parseRecipeString(recipeStr) | |||
for _, rec in ipairs(parsedList) do | |||
table.insert(recipes, rec) | |||
end | |||
i = i + 1 | |||
end | |||
local container = html.create("div"):addClass("chem-card") | |||
container:attr("style", "border-color:" .. border .. "; --card-accent:" .. color) | |||
container:tag("div"):addClass("chem-name-header"):wikitext(name) | |||
if #recipes > 0 then | |||
for idx, rec in ipairs(recipes) do | |||
local block = buildRecipeHtml(rec, idx) | |||
if block then container:node(block) end | |||
end | |||
end | |||
local effectsBlock = buildEffectsHtml(args.effects) | |||
if effectsBlock then container:node(effectsBlock) end | |||
if desc ~= "" then | |||
container:tag("div"):addClass("chem-desc"):wikitext(desc) | |||
end | |||
return tostring(container) | |||
end | end | ||
return p | return p | ||
Текущая версия от 13:44, 2 апреля 2026
Для документации этого модуля может быть создана страница Модуль:ChemCard/doc
local p = {}
local mw = mw
local html = mw.html
local function getArgs(frame)
if type(frame) == "table" and frame.args then return frame.args end
return mw.getCurrentFrame():getParent().args or {}
end
local function splitLines(s)
local t = {}
if not s then return t end
for line in mw.text.gsplit(s, "\n") do
line = mw.text.trim(line)
if line ~= "" then table.insert(t, line) end
end
return t
end
local function parseLineToItem(l)
local name, qty = mw.ustring.match(l, "^(.*)%s*%[%s*([%d,%.]+)%s*%]%s*$")
if not name then name = l; qty = "1" end
return { name = mw.text.trim(name), qty = qty or "1" }
end
local function parseRecipeString(recipeStr)
if not recipeStr or recipeStr == "" then return {} end
local lines = splitLines(recipeStr)
local recipes = {}
local currentInputs = {}
local i = 1
while i <= #lines do
local line = lines[i]
local lower = mw.ustring.lower(line)
if lower:find("смеш") then
if #currentInputs > 0 then
local outputs = {}
i = i + 1
while i <= #lines do
local nextLine = lines[i]
local nextLower = mw.ustring.lower(nextLine)
if nextLower:find("смеш") then break end
table.insert(outputs, parseLineToItem(nextLine))
i = i + 1
end
table.insert(recipes, {
inputs = currentInputs,
outputs = outputs,
action = "Смешайте"
})
currentInputs = {}
else
i = i + 1
end
else
table.insert(currentInputs, parseLineToItem(line))
i = i + 1
end
end
if #currentInputs > 0 then
table.insert(recipes, { inputs = currentInputs, outputs = {}, action = "" })
end
return recipes
end
local function buildRecipeHtml(rec, index)
if not rec or (#rec.inputs == 0 and #rec.outputs == 0) then return nil end
local container = html.create("div"):addClass("chem-recipe-block")
if index == 1 then
local heading = html.create("div"):addClass("chem-heading"):attr("data-kind", "recipe")
local hcontent = heading:tag("div"):addClass("chem-heading-content")
hcontent:tag("span"):addClass("heading-text"):wikitext("Рецепты")
hcontent:tag("span"):addClass("collapse-btn"):wikitext("развернуть")
container:node(heading)
end
local collapsible = html.create("div"):addClass("collapsible collapsed"):attr("data-kind", "recipe")
local inner = html.create("div"):addClass("chem-recipe")
-- INPUTS (левая колонка)
local inputsDiv = html.create("div"):addClass("recipe-inputs")
for _, it in ipairs(rec.inputs) do
local item = inputsDiv:tag("div"):addClass("recipe-item")
-- Используем MediaWiki-синтаксис файла — надёжно рендерится
item:wikitext('[[File:Beaker.png|36px|class=chem-beaker]] ' .. it.name .. ' [' .. it.qty .. ']')
end
inner:node(inputsDiv)
-- Действие
if rec.action ~= "" then
local actionDiv = inner:tag("div"):addClass("recipe-action")
actionDiv:wikitext(rec.action)
end
-- OUTPUTS (правая колонка)
local outputsDiv = html.create("div"):addClass("recipe-outputs")
for _, it in ipairs(rec.outputs) do
local item = outputsDiv:tag("div"):addClass("recipe-item")
item:wikitext('[[File:Beaker.png|36px|class=chem-beaker]] ' .. it.name .. ' [' .. it.qty .. ']')
end
inner:node(outputsDiv)
collapsible:node(inner)
container:node(collapsible)
return container
end
local function buildEffectsHtml(effects)
if not effects or effects == "" or effects == "Нет" then return nil end
local container = html.create("div"):addClass("chem-effects-block")
local heading = html.create("div"):addClass("chem-heading"):attr("data-kind", "effects")
local hcontent = heading:tag("div"):addClass("chem-heading-content")
hcontent:tag("span"):addClass("heading-text"):wikitext("Эффекты")
hcontent:tag("span"):addClass("collapse-btn"):wikitext("развернуть")
container:node(heading)
local collapsible = html.create("div"):addClass("collapsible collapsed"):attr("data-kind", "effects")
local inner = html.create("div"):addClass("chem-effects")
for effect in mw.text.gsplit(effects, "[;\n]") do
effect = mw.text.trim(effect)
if effect ~= "" then
inner:tag("div"):addClass("chem-effect-line"):wikitext(effect)
end
end
collapsible:node(inner)
container:node(collapsible)
return container
end
function p.card(frame)
local args = getArgs(frame)
local name = args.name or "—"
local color = args.color or "#8cf"
local border = args.border or "#444"
local desc = args.desc or ""
local recipes = {}
local i = 1
while true do
local key = i == 1 and "recipe" or ("recipe" .. i)
local recipeStr = args[key]
if not recipeStr then break end
local parsedList = parseRecipeString(recipeStr)
for _, rec in ipairs(parsedList) do
table.insert(recipes, rec)
end
i = i + 1
end
local container = html.create("div"):addClass("chem-card")
container:attr("style", "border-color:" .. border .. "; --card-accent:" .. color)
container:tag("div"):addClass("chem-name-header"):wikitext(name)
if #recipes > 0 then
for idx, rec in ipairs(recipes) do
local block = buildRecipeHtml(rec, idx)
if block then container:node(block) end
end
end
local effectsBlock = buildEffectsHtml(args.effects)
if effectsBlock then container:node(effectsBlock) end
if desc ~= "" then
container:tag("div"):addClass("chem-desc"):wikitext(desc)
end
return tostring(container)
end
return p