Модуль:Таблица рецептов
Внешний вид
Для документации этого модуля может быть создана страница Модуль:Таблица рецептов/doc
---------------------------------------------------
-- Модуль для создания таблиц рецептов в статьях
---------------------------------------------------
local m = {}
-- Интернационализация и локализация
local i18n = {
-- Параметры заголовка
headingDescription = "Описание",
headingIngredients = "Ингредиенты",
headingName = "Результат",
headingRecipe = "Рецепты [[$1|$2]]",
headingRecipeAlt = "Рецепты для [[$1|$2]]",
headingRecipeFallback = "Процесс",
-- Вспомогательные модули
moduleSlot = [[Модуль:Инвентарный слот]],
moduleUi = [[Модуль:Интерфейс]],
moduleUtils = [[Модуль:Специальные утилиты]],
moduleMods = [[Модуль:Модификации]],
-- Разделители
separator = ' +',
setSeparator = " или",
-- Описание таблицы в HTML-атрибуте data-description
tableDescription = "Рецепты $2",
tableDescriptionAlt = "Рецепты для $2",
tableDescriptionFallback = "Рецепты обработки"
}
-- Зависимости
local slot = require(i18n.moduleSlot)
local prefixesMatch = slot.i18n.prefixesMatch
local mergeList = require(i18n.moduleUtils).mergeList
local modIds = mw.loadData(i18n.moduleMods)
-- Служебная функция: Преобразует все аргументы входа и выхода, сохраняя
-- все фреймы в единую таблицу, и сохраняя ссылки на псевдонимы.
-- Одинаковые слоты повторно используют одну и ту же таблицу, что позволяет
-- сравнивать их подобно строкам.
local function parseRecipeArgs(args, ingredientArgVals, outputArgs, defaultMod)
-- Аргументы в исходном виде
local recipeArgs = {}
for _, arg in pairs(ingredientArgVals) do
recipeArgs[arg] = args[arg]
end
for _, arg in pairs(outputArgs) do
recipeArgs[arg] = args[arg]
end
-- Обработка аргументов
local parsedFrameInput = {}
local parsedRecipeArgs = {}
for arg, frameInput in pairs(recipeArgs) do
if frameInput then -- есть содержимое аргумента?
local randomise
for _, oArg in pairs(outputArgs) do
if arg == oArg then
-- Отключаем рандомизацию для аргументов выхода
randomise = 'никогда'
break
end
end
-- Нормализация
local rawFrames = frameInput
if not frameInput[1] then
rawFrames = slot.convertFrameText(frameInput, defaultMod)
end
local frameText = slot.stringifyFrames(rawFrames)
local frames = not randomise and parsedFrameInput[frameText]
if not frames then
frames = slot.expandAliases(rawFrames, randomise, true)
parsedFrameInput[frameText] = frames
end
parsedRecipeArgs[arg] = frames
end
end
return parsedRecipeArgs
end
-- Создаёт ссылку с выносом префикса за её пределы
function m.prefixedLink(name, mod)
-- Ищем и выносим префиксы
local prefix
for _, prefixMatch in pairs(prefixesMatch) do
prefix = mw.ustring.match(name, '^' .. prefixMatch .. ' ') or ''
if prefix ~= '' then
name = name:sub(#prefix + 1)
-- Повторяем процесс, если есть ещё префикс «невощёный»
local unwaxed = mw.ustring.match(name, '^' .. prefixesMatch.unwaxed .. ' ')
if unwaxed then
prefix = prefix .. unwaxed
name = name:sub(#unwaxed + 1)
end
break
end
end
-- Формируем цель ссылки
local page = name
if mod then
local mod = modIds[mod] or mod
page = mw.ustring.gsub(name, '^%l', mw.ustring.upper)
page = slot.i18n.modLink:gsub('%$1', mod):gsub('%$2', page)
end
-- Формируем и возвращаем ссылку
return table.concat{ prefix, '[[', page, '|', name, ']]' }
end
-- Преобразует множество слотов во множества предметов, соответствующих
-- фреймам тех слотов, используя исходное название псевдонима, если оно есть.
function m.makeItemSets(argVals, parsedArgs)
-- Уже зарегистрированные фреймы
local usedItems = {}
-- вспомогательная функция
local function addItem(items, arg, frame, alias)
if alias then -- псевдоним
frame = alias["фрейм"]
end
-- Уникальное название фрейма
local uniqName = (frame.mod or '') .. ':' .. frame.name
if not usedItems[uniqName] then
usedItems[uniqName] = true
items[#items + 1] = frame
end
return alias and alias["длина"] or 1
end
-- Множества предметов
local itemSets = {}
local i = 1
for _, arg in ipairs(argVals) do
local frames = parsedArgs[arg]
if frames then
-- Предметы во множестве
local items = {}
local frameNum = 1
while frameNum <= #frames do
-- Цикл while используется, так как могут проскакиваться несколько фреймов,
-- принадлежащих псевдониму
local frame = frames[frameNum]
if frame[1] then
-- контейнер подфреймов
local subframeNum = 1
while subframeNum <= #frame do
local subframe = frame[subframeNum]
if subframe.name ~= '' then
local alias = frame["ссылканапсевдонимы"] and frame["ссылканапсевдонимы"][subframeNum]
subframeNum = subframeNum + addItem(items, arg, subframe, alias)
else
subframeNum = subframeNum + 1
end
end
frameNum = frameNum + 1
elseif frame.name ~= '' then
local alias = frames["ссылканапсевдонимы"] and frames["ссылканапсевдонимы"][frameNum]
frameNum = frameNum + addItem(items, arg, frame, alias)
else
frameNum = frameNum + 1
end
end
-- Добавляем множество, если оно не пустое
if #items > 0 then
itemSets[i] = items
i = i + 1
end
end
end
return itemSets
end
-- Создаёт по множествам предметов ссылки для колонок названий и ингредиентов
-- с использованием разделителей и возможным удалением префиксов «любой» и
-- «соответствующий».
function m.makeItemLinks(itemSets, removePrefixes)
local links = {}
for i, itemSet in ipairs(itemSets) do
local linkSet = {}
for i2, item in ipairs(itemSet) do
local name = item.name
if removePrefixes then
-- Удаляем префиксы
name = mw.ustring.gsub(name, '^' .. prefixesMatch.any .. ' ', '')
name = mw.ustring.gsub(name, '^' .. prefixesMatch.matching .. ' ', '')
-- Переводим первую букву в заглавную форму
name = mw.ustring.gsub(name, '^%l', mw.ustring.upper)
end
-- Формируем ссылку.
-- Если она содержит слово «или», то разбиваем её.
local disjunctionA, disjunctionB = mw.ustring.match(name, "(.-) или (.+)")
if disjunctionA then
linkSet[i2] = m.prefixedLink(disjunctionA, item.mod)
.. ' или '
.. m.prefixedLink(disjunctionB, item.mod)
else
linkSet[i2] = m.prefixedLink(name, item.mod)
end
end
links[i] = table.concat(linkSet, i18n.setSeparator .. '<br>')
end
return table.concat(links, i18n.separator .. '<br>')
end
-- Создать заглавие таблицы
function m.makeHeader(recipeSettings, class, showName, showDescription, multirow, multipleRecipeTypes)
-- CSS-класс
class = class or ''
-- Колонка с названиями
local nameCell = ''
if showName then
nameCell = '!!' .. i18n.headingName
end
-- Колонка с описанием
local descriptionCell = ''
if showDescription then
descriptionCell = '!!class="unsortable"|' .. i18n.headingDescription
end
local recipeAttribs = ''
if multirow then
-- несколько колонок
class = 'sortable' .. class
recipeAttribs = 'class="unsortable"|'
end
-- Заголовок процесса
local headingRecipe = i18n.headingRecipeFallback
local processLinkPrefix
if recipeSettings["модификация"] then
processLinkPrefix = recipeSettings["модификация"] .. '/'
else
processLinkPrefix = ''
end
if not multipleRecipeTypes then
if (recipeSettings["тип"] or recipeSettings["обработчик"]) and recipeSettings["типа"] then
local processLink = processLinkPrefix .. (recipeSettings["тип"] or recipeSettings["обработчик"])
headingRecipe = i18n.headingRecipe:gsub('%$1', processLink)
:gsub('%$2', recipeSettings["типа"])
elseif recipeSettings["обработчик"] and recipeSettings["обработчика"] then
headingRecipe = i18n.headingRecipeAlt:gsub('%$1', processLinkPrefix .. recipeSettings["обработчик"])
:gsub('%$2', recipeSettings["обработчика"])
end
end
local tableDescription = i18n.tableDescriptionFallback
if not multipleRecipeTypes then
if recipeSettings["типа"] then
tableDescription = i18n.tableDescription:gsub('%$2', recipeSettings["типа"])
elseif recipeSettings["обработчика"] then
tableDescription = i18n.tableDescriptionAlt:gsub('%$2', recipeSettings["обработчика"])
end
end
local header = table.concat({
' {| class="wikitable recipe-table collapsible ' .. class .. '" data-description="' .. tableDescription .. '"',
'!' .. i18n.headingIngredients .. '!!' ..
recipeAttribs .. headingRecipe ..
nameCell .. descriptionCell
}, '\n')
return header
end
-- Создать содержимое для ячейки с названием
function m.makeNameCell(name, outputArgs, parsedRecipeArgs)
return name or m.makeItemLinks(m.makeItemSets(outputArgs, parsedRecipeArgs), true)
end
-- Создать содержимое для ячейки с ингредиентами
function m.makeIngredientsCell(ingredients, itemSets)
return ingredients or m.makeItemLinks(itemSets)
end
-- Собственно создать таблицу (входная точка модуля).
-- Использует переменные DPL для группирования нескольких вызовов в одну таблицу.
-- Также возвращает список уникальных ингредиентов, который может использоваться
-- для обеспечения работы шаблона «Использование для крафта».
function m.table(args, settings)
local f = mw.getCurrentFrame()
-- продолжаем ли таблицу?
local multirow = f:callParserFunction( '#dplvar', 'recipetable-multirow' )
if multirow == '' then
multirow = nil
end
-- Параметры главы и подвала таблицы
local showHead = args["глава"]
local showFoot = args["подвал"]
if multirow then
-- игнорируем параметр главы, если таблица продолжается
showHead = nil
elseif showHead and not showFoot then
-- начало таблицы
multirow = true
f:callParserFunction('#dplvar:set', 'recipetable-multirow', '1')
else
-- единичный ряд таблицы
showHead = true
showFoot = true
end
-- Контроль отображения названия или описания
local showName = args["показатьназвание"] or args["показатьимя"]
local showDescription = args["показатьописание"]
if multirow then
if showHead then
-- Указываем, что нужно добавить колонку с описанием
showName = showName or '1'
f:callParserFunction('#dplvar:set', 'recipetable-name', showName, 'recipetable-description', showDescription)
else
showName = f:callParserFunction('#dplvar', 'recipetable-name')
showDescription = f:callParserFunction('#dplvar', 'recipetable-description')
end
end
if showName ~= '1' then
showName = nil
end
if showDescription == '' then
showDescription = nil
end
-- Таблица или её часть
local out = {}
-- Заголовок
if showHead then
out[1] = m.makeHeader(settings, args["класс"], showName, showDescription, multirow, args["несколько обработок"])
end
-- Разбор аргументов
local ingredientArgVals = settings["аргументы ингредиентов"]
local outputArgs = settings["аргументы выхода"]
local parsedRecipeArgs = args
local defaultMod = args["мод"] or args["Мод"] or settings["модификация"]
if not args["обработанный"] then
parsedRecipeArgs = parseRecipeArgs(args, ingredientArgVals, outputArgs, defaultMod)
end
-- Клетки
local cells = {}
-- Ингредиенты
local ingredientsItemSets = m.makeItemSets(ingredientArgVals, parsedRecipeArgs)
cells[1] = '|' .. m.makeIngredientsCell(args["ингредиенты"], ingredientsItemSets)
-- Собственно обработка
local funcUi = settings["функция интерфейса"]
if type(funcUi) == "string" then
local moduleUi = settings["модуль интерфейса"] or i18n.moduleUi
funcUi = require(moduleUi)[funcUi]
elseif type(funcUi) ~= "function" then
error("Необходима ссылка на функцию интерфейса или её название в модуле")
end
cells[2] = '|style="text-align:center"|' .. funcUi(args)
-- Название
if showName then
cells[#cells + 1] = '|class="recipe-table-name"|' .. m.makeNameCell(args["название"], outputArgs, parsedRecipeArgs)
end
-- Описание
if showDescription then
cells[#cells + 1] = '|' .. ( args["описание"] or '' )
end
out[#out + 1] = table.concat( cells, '\n' )
-- Подвал
out[#out + 1] = showFoot and '|}' or ''
if showFoot then
f:callParserFunction('#dplvar:set',
'recipetable-multirow', '',
'recipetable-name', '',
'recipetable-description', ''
)
end
return table.concat( out, '\n|-\n' ), ingredientsItemSets
end
return m