Шаблон:Карточка: различия между версиями

Новая страница: «<includeonly>{{#invoke:Карточка|main |kind = role |sep = , |skipSectionRows = 3 |title = {{#if: {{{название}}} | {{{название}}} | {{BASEPAGENAME}} }} |image = {{{картинка}}} |caption = {{{подпись|}}} |style = --druid-color-contrast:{{{цветФона|var(--color-contrast-dark)}}}; |sections = Общие, Предмет, Роль, Игровой режим, Проект, Игра, Раса, Характе...»
 
Нет описания правки
 
Строка 1: Строка 1:
<includeonly>{{#invoke:Карточка|main
-- DRUID Infobox.
|kind    = role
-- source: https://support.wiki.gg/wiki/Module:Infobox
|sep    = ,
-- license: CC BY-SA 4.0
|skipSectionRows = 3
|title  = {{#if: {{{название}}} | {{{название}}} | {{BASEPAGENAME}} }}
|image  = {{{картинка}}}
|caption = {{{подпись|}}}
|style  = --druid-color-contrast:{{{цветФона|var(--color-contrast-dark)}}};


|sections = Общие, Предмет, Роль, Игровой режим, Проект, Игра, Раса, Характеристики расы, Корпорация, Техническая информация
-- version 0.1.7


<!------
--------------------------------------
Общие
-- User settings, you can modify these
------->
--------------------------------------
|Общие = Тип
|Общие_nolabel = yes


|Тип = {{#if: {{{тип|}}}| {{#invoke:CategoryFormatter|main|{{{тип}}}}} | }}
-- if you want to not always use divs in your wiki (as opposed to tables), you can change this default
-- just remember to change it back each time you update from the main "branch" on the support wiki!
-- you can also control it per infobox with `|useDivs=yes` or `|useDivs=no`
local USE_DIVS = false -- `false` or `true`


<!------
-- default value to show if a param is missing in some but not all tabs.
Предметы
-- set to `nil` (not in quotes) to remove such rows altogether in the tabs where they're missing
------->
local TABBED_NONEXIST = nil -- `''` or `nil` or `'N/A'` etc. Don't put nil in quotes.
|Предмет = Описание, Размер, Наносит повреждения, Стоимость
local SKIP_SECTIONS_DEFAULT = 2


|Описание = {{пп|{{{прОписание|_}}}| НЕ ПИШИ СЮДА | ''{{#invoke:Entity Lookup|getdesc|{{{id}}}}}'' }}
---------------------------------------------------------------------------
|Размер = {{пп|{{{прРазмер|_}}}| {{{прРазмер}}} | {{#invoke:Prototypes/Предмет/Размер|main|{{{id}}}}} }}
-- Do not modify anything below this line unless you know what you're doing
|Наносит повреждения = {{пп|{{{прУрон|_}}}| {{{прУрон}}} | {{#invoke:Prototypes/Оружия|main|melee|{{{id}}}}} }}
---------------------------------------------------------------------------
|Стоимость = {{пп|{{{стоимость|_}}}| {{{стоимость}}} | {{#invoke:Prototypes/Механика/Стоимость|main|{{{id}}}}} }}


<!------
local h = {}
Роли
local p = {}
------->
local hooks = {}
|Роль = Фракция, Цель, Отдел, Подчиняется, Обязанности, Доступ, Необходимое время, Руководства


|Фракция = {{пп|{{{рольФракция|_}}}| {{{рольФракция}}} | нет }}
function p.arraymap(frame)
|Цель = {{пп|{{{рольЦель|_}}}| {{{рольЦель}}} | нет }}
-- a lua implementation of Page Forms' arraymap
|Отдел = {{пп|{{{рольОтдел|_}}}| {{{рольОтдел}}} | нет }}
local args = h.overwrite()
|Подчиняется = {{пп|{{{рольГлава|_}}}| {{{рольГлава}}} | {{#invoke:Prototypes/Роль|main|supervisors|{{{id}}}}} }}
local items = h.split(args[1], args[2] or ',')
|Обязанности = {{пп|{{{рольОбязанности|_}}}| {{{рольОбязанности}}} | ''{{#invoke:Prototypes/Роль|main|description|{{{id}}}}}'' }}
for i, item in ipairs(items) do
|Доступ = {{пп|{{{рольДоступ|_}}}| {{{рольДоступ}}} | {{#invoke:Prototypes/Роль|main|access|{{{id}}}}} }}
items[i] = args[4]:gsub(args[3], item)
|Необходимое время = {{пп|{{{рольВремя|_}}}| {{{рольВремя}}} | {{#invoke:Prototypes/Роль|main|requirements|{{{id}}}}} }}
end
|Руководства = {{пп|{{{руководства|_}}}| {{{руководства}}} | нет }}
return table.concat(items, args[5] or ',')
end


<!------
function p.preprocess(frame)
Игровые режимы
  local rawText = frame.args[1] or frame:getParent().args[1]
------->
  return frame:preprocess(rawText)
|Игровой режим = Особые роли, Соотношение особых ролей к обычным, Максимум особых ролей, Минимум игроков, Случайные события режима, Дополнительные режимы
end


|Особые роли = {{пп|{{{особыеРоли|_}}}| {{{особыеРоли}}} | нет }}
function p.main(frame)
|Соотношение особых ролей к обычным = {{пп|{{{соотношение|_}}}| {{{соотношение}}} | нет }}
h.registerHooks()
|Максимум особых ролей = {{пп|{{{максимумРолей|_}}}| {{{максимумРолей}}} | нет }}
h.increment()
|Минимум игроков = {{пп|{{{минимумИгроков|_}}}| {{{минимумИгроков}}} | нет }}
local args = h.overwrite()
|Случайные события режима = {{пп|{{{случайныеСобытия|_}}}| {{{случайныеСобытия}}} | нет }}
local sep = args.sep or ','
|Дополнительные режимы = {{пп|{{{допРежимы|_}}}| {{{допРежимы}}} | нет }}
h.castArgs(args, sep)
local skip = tonumber(args.skipSections) or SKIP_SECTIONS_DEFAULT
h.skipSections = skip
h.printCount = 0
if h.castBool(args.setmainimage or 'yes') then
h.setMainImage(args.images[1])
end
-- suggest to use HIDDENCAT here; will be used for maintenance & gadget imports
local output = h.makeInfobox(args, sep)
local finalOutput = tostring(output)
return frame:preprocess(finalOutput)
end


<!------
function h.registerHooks()
Проекты
if not mw.title.new('Module:Infobox/Hooks').exists then return end
------->
hooks = require('Module:Infobox/Hooks')
|Проект = Основатели проекта, Количество проектов, Количество серверов
end


|Основатели проекта = {{пп|{{{основателиПроекта|_}}}| {{{основателиПроекта}}} | нет }}
function h.runHook(key, ...)
|Количество проектов = {{пп|{{{количествоПроектов|_}}}| {{{количествоПроектов}}} | нет }}
if hooks[key] then
|Количество серверов = {{пп|{{{количествоСерверов|_}}}| {{{количествоСерверов}}} | нет }}
hooks[key](...)
end
end


<!------
function h.increment()
Игра
-- optional use of VariablesLua for better compatibility
------->
local VariablesLua = mw.ext.VariablesLua
|Игра = Разработчик, Издатель, Дата выхода, Начало разработки, Режимы, Платформа, Системные требования, Сайт
if VariablesLua == nil then
local res
-- try to fall back to normal Variables
res, h.counter = pcall(
function()
return mw.getCurrentFrame():callParserFunction('#var', {'DRUID_INFOBOX_ID', 0}) + 1
end
)
if res then
mw.getCurrentFrame():callParserFunction('#vardefine', {'DRUID_INFOBOX_ID', h.counter})
else
-- else use a random number so at least there's some unique id
h.counter = math.random(100000000000000000) -- random integer
end
else
h.counter = VariablesLua.var('DRUID_INFOBOX_ID', 0) + 1
VariablesLua.vardefine('DRUID_INFOBOX_ID', h.counter)
end
end


|Разработчик = {{пп|{{{разработчик|_}}}| {{{разработчик}}} | нет }}
function h.castArgs(args, sep)
|Издатель = {{пп|{{{издатель|_}}}| {{{издатель}}} | нет }}
h.runHook('onCastArgsStart', args, sep, args.kind)
|Дата выхода = {{пп|{{{датаВыхода|_}}}| {{{датаВыхода}}} | нет }}
args.tabs = h.split(args.tabs or args.image_labels, sep)
|Начало разработки = {{пп|{{{началоРазработки|_}}}| {{{началоРазработки}}} | нет }}
args.images = h.getImages(args, sep)
|Режимы = {{пп|{{{режимы|_}}}| {{{режимы}}} | нет }}
args.sections = h.split(args.sections, sep)
|Платформа = {{пп|{{{платформа|_}}}| {{{платформа}}} | нет }}
for _, section in ipairs(args.sections) do
|Системные требования = {{пп|{{{системныеТребования|_}}}| {{{системныеТребования}}} | нет }}
if h.castBool(args[section .. '_isdata']) then
|Сайт = {{пп|{{{сайт|_}}}| {{{сайт}}} | нет }}
args[section .. 'Data'] = args[section]
args[section] = section .. 'Data'
args[section .. 'Data_nolabel'] = 'true' -- will be cast later
end
args[section] = h.split(args[section], sep)
args[section .. '_tabs'] = h.split(args[section .. '_tabs'], sep)
if #args.tabs > 0 and #args[section .. '_tabs'] > 0 then
error(('You cannot specify |tabs= and |%s= at the same time, please pick one'):format(section .. '_tabs'))
end
end
if args.useDivs then
USE_DIVS = h.castBool(args.useDivs)
end
-- this would be in the outer scope, but we're hiding it
h.entityType = USE_DIVS and 'div' or 'table' -- key of h.htmlEntities
h.runHook('onCastArgsEnd', args, sep, args.kind)
end


<!------
function h.getImages(args, sep)
Раса
if args.image and not args.images then
------->
args.images = args.image
|Раса = Родной мир, Система, Средний рост, Средний вес, Время жизни
end
if args.images then
return h.split(args.images, sep)
end
if not args.tabs then return {} end
local ret = {}
for _, key in ipairs(args.tabs) do
if args[key .. '_image'] then
ret[#ret+1] = args[key .. '_image']
end
end
return ret
end


|Родной мир = {{пп|{{{расаРоднойМир|_}}}| {{{расаРоднойМир}}} | нет }}
function h.setMainImage(file)
|Система = {{пп|{{{расаСистема|_}}}| {{{расаСистема}}} | нет }}
if h.counter > 1 then return end
|Средний рост = {{пп|{{{расаСреднийРост|_}}}| {{{расаСреднийРост}}} | нет }}
if not file then return end
|Средний вес = {{пп|{{{расаСреднийВес|_}}}| {{{расаСреднийВес}}} | нет }}
local fileText = file:gsub('.-:', '')
|Время жизни = {{пп|{{{расаВремяЖизни|_}}}| {{{расаВремяЖизни}}} | нет }}
fileText = fileText:gsub('^([^|%]]+).*', '%1')
-- setmainimage is guaranteed to exist on wiki.gg but may not exist on other wikis
-- it's not a crucial piece of functionality so we'll fail silently if it doesn't exist
pcall(function() mw.getCurrentFrame():callParserFunction{
name = '#setmainimage',
args = { fileText },
} end)
end


|Характеристики расы = Наносит с руки, Здоровье, Модификатор урона
function h.makeInfobox(args, sep)
local out = mw.html.create(h.getTag('container'))
:addClass('druid-infobox')
:addClass('druid-container')
:addClass('noexcerpt')
:addClass(args.class) -- warning: class can be nil, don't concat anything
:attr('id', args.id or ('druid-container-' .. h.counter))
if args.style then
out:cssText(args.style)
end


|Наносит с руки = {{пп|{{{наноситРукой|_}}}| {{{наноситРукой}}} | нет }}
h.runHook('onMakeOutputStart', out, args)
|Здоровье = {{пп|{{{здоровье|_}}}| {{{здоровье}}} | нет }}
if args.kind then out:addClass('druid-container-' .. h.escape(args.kind)) end
|Модификатор урона = {{пп|{{{модификаторУрона|_}}}| {{{модификаторУрона}}} | нет }}
h.printTitle(out, args)
h.printImages(out, args.images, args)
for _, section in ipairs(args.sections) do
local cols = args[section .. '_columns']
local makeSection = cols and h.makeGridSection or h.makeSection
local secNode = makeSection(section, args[section], args, tonumber(cols))
if secNode then
out:node(secNode)
end
end
h.runHook('onMakeOutputEnd', out, args)
return out
end


<!------
function h.printTitle(out, args)
Государство
local tabs = args.tabs
------->
if not tabs or #tabs == 0 then
|Государство = Официальное название, Расположение, Столица, Этнохроним, Языки, Численность населения, Форма правления, Глава правительства, Глава государства, Валюта
h.printSimpleTitle(out, args)
return
end
if not h.hasComplexData('title', tabs, args) then
h.printSimpleTitle(out, args)
return
end
local node = h.printTitleWrapper(out)
h.printTabbedDataItem(node, 'title', tabs, args)
end


|Официальное название = {{пп|{{{официальное название|_}}}| {{{официальное название}}} | нет }}
function h.printSimpleTitle(out, args)
|Расположение = {{пп|{{{расположение|_}}}| {{{расположение}}} | нет }}
if args.title then
|Столица = {{пп|{{{столица|_}}}| {{{столица}}} | нет }}
local node = h.printTitleWrapper(out)
|Этнохроним = {{пп|{{{этнохроним|_}}}| {{{этнохроним}}} | нет }}
node:wikitext("{{ucfirst:" .. args.title .. "}}")
|Языки = {{пп|{{{языки|_}}}| {{{языки}}} | нет }}
end
|Численность населения = {{пп|{{{численность населения|_}}}| {{{численность населения}}} | нет }}
end
|Форма правления = {{пп|{{{форма правления|_}}}| {{{форма правления}}} | нет }}
|Глава правительства = {{пп|{{{глава правительства|_}}}| {{{глава правительства}}} | нет }}
|Глава государства = {{пп|{{{глава государства|_}}}| {{{глава государства}}} | нет }}
|Валюта = {{пп|{{{валюта|_}}}| {{{валюта}}} | нет }}


<!------
function h.printTitleWrapper(out)
Корпорация
return out:tag(h.getTag('titleOuter'))
------->
:tag(h.getTag('titleInner'))
|Корпорация = Основание, Основатели компании, Ключевые фигуры, Отрасль, Продукция
:addClass('druid-title')
:attr('colspan', 2)
end


|Основание = {{пп|{{{основание|_}}}| {{{основание}}} | нет }}
function h.printTabbedDataItem(node, item, tabs, args)
|Основатели компании = {{пп|{{{основатели|_}}}| {{{основателиКомпании}}} | нет }}
-- hasData isn't used in the title case but we will need to track this
|Ключевые фигуры = {{пп|{{{ключевыеФигуры|_}}}| {{{ключевыеФигуры}}} | нет }}
-- when we're printing section data later on
|Отрасль = {{пп|{{{отрасль|_}}}| {{{отрасль}}} | нет }}
-- so we'll just track it always
|Продукция = {{пп|{{{продукция|_}}}| {{{продукция}}} | нет }}
local hasData = false
for i, label in ipairs(tabs) do
local div = node:tag('div')
:addClass('druid-toggleable-data')
:addClass('druid-toggleable')
:attr('data-druid', h.counter .. '-' .. i)
:attr('data-druid-tab-key', label)
if h.getTabbedContent(args, label, item) then
hasData = true
div:wikitext('\n\n' .. h.getTabbedContent(args, label, item))
div:addClass('druid-toggleable-data-nonempty')
else
div:addClass('druid-toggleable-data-empty')
end
if i == 1 then div:addClass('focused') end
end
return hasData
end


<!------
function h.printImages(out, images, args)
Прочее
if #images == 0 and #args.tabs == 0 then return end
------->
-- burden is on the user to format this as an image. this should be done in the infobox template,
|Техническая информация = Внутренний id
-- with something like |image={{#if:{{{image|}}}|[[File:{{{image|}}}{{!}}300px{{!}}link=]]}}
|Внутренний id = {{пп|{{{id|_}}}| <code>{{{id}}}</code> | нет }}
local td = out:tag(h.getTag('section'))
}}</includeonly><!--
:addClass('druid-section-container')
:tag(h.getTag('cell'))
:attr('colspan', 2)
local tabs = args.tabs
local tabTexts = h.getImageTabTexts(tabs, images, args)
h.printTabs(td, tabs, tabTexts, false, args)
if #images == 0 then return end
if #images == 1 then
td:addClass('druid-main-image')
:wikitext(images[1])
if args.caption then
td:tag('div')
:addClass('druid-main-image-caption')
:wikitext(args.caption)
end
return
end
td:addClass('druid-main-images')
local imagesContainer = td:tag('div')
:addClass('druid-main-images-files')
for i, image in ipairs(images) do
local container = imagesContainer:tag('div')
:addClass('druid-main-images-file')
:addClass('druid-toggleable')
:attr('data-druid', h.counter .. '-' .. i)
:wikitext(image)
:attr('data-druid-tab-key', tabs[i])
local labelText
if tabs[i] then
labelText = args[tabs[i] .. '_label'] or tabs[i]
else
labelText = '[[Category:Infoboxes missing image labels]]Image ' .. i
end
if args[labelText .. '_caption'] then
container:tag('div')
:addClass('druid-main-images-caption')
:wikitext(args[labelText .. '_caption'])
end
if i == 1 then
container:addClass('focused')
end
end
end


--><noinclude>{{doc}}</noinclude>
function h.getImageTabTexts(tabs, images, args)
if #tabs == 0 and #images <= 1 then return {} end
local texts = {}
local i = 1
while images[i] or tabs[i] do
if tabs[i] then
texts[i] = args[tabs[i] .. '_label'] or tabs[i]
else
texts[i] = '[[Category:Infoboxes missing image labels]]Image ' .. i
end
i = i + 1
end
return texts
end
 
function h.printTabs(td, tabs, texts, isSection, args)
if #texts == 0 then return end
local container = td:tag('div')
:addClass('druid-main-images-labels')
:addClass('druid-tabs')
if isSection then
container:addClass('druid-section-tabs')
end
for i, item in ipairs(tabs) do
local label = container:tag('div')
:addClass('druid-main-images-label')
:addClass('druid-tab')
:addClass('druid-toggleable')
:attr('data-druid', h.counter .. '-' .. i)
:wikitext(texts[i])
:attr('data-druid-tab-key', item)
if isSection then
label:addClass('druid-section-tab')
else
label:addClass('druid-title-tab')
end
if i == 1 then
label:addClass('focused')
end
-- this can be null, don't concat anything here
label:addClass(args[item .. '_class'])
end
end
 
function h.makeGridSection(section, sectionFields, args, numCols)
local numItems = h.countItems(sectionFields, section, args)
if numItems == 0 then return end
local node = mw.html.create(h.getTag('section'))
:addClass('druid-section-container')
h.printSectionHeader(node, section, args)
h.printSectionTabs(node, section, args)
local tr = node:tag(h.getTag('row'))
:attr('data-druid-section-row', h.escape(section))
if args[section .. '_collapsed'] then
tr:addClass('druid-collapsed')
end
local grid = tr:tag(h.getTag('cell'))
:attr('colspan', 2)
:addClass('druid-grid-section')
:addClass('druid-grid-section-' .. h.escape(section))
:addClass(args[section .. '_class']) -- warning: class can be nil, don't concat anything
:tag('div')
:addClass('druid-grid')
local row, col, i = 1, 1, 1
local sizeOfLastRow = numItems % numCols
local lcm = h.getNumGridCols(numItems, sizeOfLastRow, numCols)
grid:css('grid-template-columns', ('repeat(%s, 1fr)'):format(lcm))
local size = lcm / numCols
for _, item in ipairs(sectionFields) do
local node = mw.html.create('div')
local shouldPrint = h.printData(node, item, section, args)
if shouldPrint then
if i == numItems - sizeOfLastRow + 1 then
size = lcm / sizeOfLastRow
end
i = i + 1
local gStart = (col - 1) * size + 1
local gEnd = (col) * size + 1
local itemContainer = grid:tag('div')
:addClass('druid-grid-item')
:addClass('druid-grid-item-' .. h.escape(item))
:addClass(args[item .. '_class']) -- warning: class can be nil, don't concat anything
:css('grid-column', ('%s / %s'):format(gStart, gEnd))
:css('grid-row', row)
if not h.castBool(args[item .. '_nolabel']) then
h.printLabel(itemContainer:tag('div'), item, args)
end
itemContainer:node(node)
if col == numCols then
row = row + 1
col = 1
else
col = col + 1
end
end
end
return node
end
 
function h.makeSection(section, sectionFields, args)
if section == '' then return end -- bruteforce fix for trailing commas
local shouldPrint = false
local container = mw.html.create(h.getTag('section'))
:addClass('druid-section-container')
:addClass(args[section .. '_class']) -- warning: class can be nil, don't concat anything
h.printSectionHeader(container, section, args)
h.printSectionTabs(container, section, args)
for _, item in ipairs(sectionFields) do
local node = mw.html.create(h.getTag('cell'))
local shouldPrintItem = h.printData(node, item, section, args)
if shouldPrintItem then
h.printCount = h.printCount + 1
shouldPrint = true
local tr = container:tag(h.getTag('row'))
:addClass('druid-row')
:addClass('druid-row-' .. h.escape(item))
:addClass(args[item .. '_class']) -- warning: class can be nil, don't concat anything
:attr('data-druid-section-row', h.escape(section))
if args[section .. '_collapsed'] then
tr:addClass('druid-collapsed')
end
if h.castBool(args[item .. '_wide']) or h.castBool(args[item .. '_nolabel']) then
node
:attr('colspan', 2)
:addClass('druid-data-wide')
else
h.printLabel(tr:tag(h.getTag('label')), item, args)
end
tr:node(node)
end
end
if not shouldPrint then return nil end
return container
end
 
function h.countItems(sectionFields, section, args)
local numItems = 0
for _, v in ipairs(sectionFields) do
-- we aren't actually printing here, but we're finding out if we should print anything
-- because we need the count of columns before we print anything in grid data
if h.printData(mw.html.create(), v, section, args) then
numItems = numItems + 1
end
end
return numItems
end
 
function h.getNumGridCols(numItems, sizeOfLastRow, numCols)
if not numCols then return numItems, 1 end
if numItems < numCols then return numItems, 1 end
if sizeOfLastRow == 0 then
return numCols, 1
end
local a, b = sizeOfLastRow, numCols
while b ~= 0 do
a, b = b, a % b
end
local lcm = sizeOfLastRow * numCols / a
return lcm
end
 
function h.printLabel(node, item, args)
local text = args[item .. '_display'] or args[item .. '_label'] or item
return node
:addClass('druid-label')
:addClass('druid-label-' .. h.escape(item))
:wikitext("{{ucfirst:" .. text .. "}}")
end
 
function h.printData(node, item, section, args)
-- prints data to the node
-- and also returns whether the item is nonempty or not
local hasData = false
local sectionTabs = args[section .. '_tabs']
local tabs = args.tabs
if sectionTabs and #sectionTabs > 0 then
tabs = sectionTabs
end
if not tabs or #tabs == 0 then
return h.printSimpleData(node, item, args)
end
if not h.hasComplexData(item, tabs, args) then
return h.printSimpleData(node, item, args)
end
hasData = hasData or h.printTabbedDataItem(node, item, tabs, args)
if hasData then
node:addClass('druid-data')
end
return hasData
end
 
function h.getTabbedContent(args, label, item)
return args[label .. '_' .. item] or args[item] or TABBED_NONEXIST
end
 
function h.printSimpleData(node, item, args)
if args[item] and type(args[item]) ~= 'string' then
error(("Invalid use of field %s as both a section and a data value"):format(item))
end
if not args[item] then return false end
node:addClass('druid-data')
:addClass('druid-data-' .. h.escape(item))
:addClass('druid-data-nonempty')
:wikitext("\n\n{{ucfirst:" .. args[item] .. "}}")
return true
end
 
function h.hasComplexData(item, tabs, args)
for _, v in ipairs(tabs) do
if args[v .. '_' .. item] then return true end
end
return false
end
 
function h.printSectionHeader(node, section, args)
local fields = args[section] or {}
local hasContent = args[section .. '_columns']
and (h.countItems(fields, section, args) > 0)
  or (function()
for _, item in ipairs(fields) do
if h.printData(mw.html.create(), item, section, args) then
return true
end
end
return false
end)()
if not hasContent then return end
 
if h.castBool(args[section .. '_nolabel']) then return end
 
h.printCount = h.printCount + 1
 
if h.printCount <= h.skipSections then
return
end
 
local tr = node:tag(h.getTag('row'))
:attr('data-druid-section', h.escape(section))
:addClass('data-druid-section')
local th = tr:tag(h.getTag('sectionTitle'))
:attr('colspan', 2)
:addClass('druid-section')
:addClass('druid-section-' .. h.escape(section))
if args[section .. '_collapsible'] or args[section .. '_collapsed'] then
tr:addClass('druid-collapsible')
if args[section .. '_collapsed'] then
tr:addClass('druid-collapsible-collapsed')
end
end
 
local emptySections = {}
for _, label in ipairs(args.tabs) do
local hasLabel = false
for _, item in ipairs(args[section] or {}) do
if h.getTabbedContent(args, label, item) then
hasLabel = true
end
end
if not hasLabel then emptySections[label] = true end
end
if not next(emptySections) then
th:wikitext("{{ucfirst:" .. (args[section .. '_label'] or section) .. "}}")
return
end
for i, label in ipairs(args.tabs) do
local div = th:tag('div')
:addClass('druid-toggleable-heading')
:addClass('druid-toggleable')
:attr('data-druid', h.counter .. '-' .. i)
:wikitext("{{ucfirst:" .. (args[section .. '_label'] or section) .. "}}")
-- we are going to print the section content even in empty nodes
-- for compatibility with browsers without :has, where hiding empty rows won't happen
if emptySections[label] then
div:addClass('druid-toggleable-heading-empty')
end
if i == 1 then
div:addClass('focused')
end
end
end
 
function h.printSectionTabs(node, section, args)
local tabs = args[section .. '_tabs']
if not tabs or #tabs == 0 then return end
local tr = node:tag(h.getTag('sectionTabsOuter'))
:attr('data-druid-section', h.escape(section))
:addClass('data-druid-section')
local th = tr:tag(h.getTag('sectionTabs'))
:attr('colspan', 2)
:addClass('druid-section-tabs')
:addClass('druid-section-tabs-' .. h.escape(section))
local texts = {}
for i, item in ipairs(tabs) do
texts[i] = args[item .. '_label'] or item
end
h.printTabs(th, tabs, texts, true, args)
end
 
----------------------------
-- general utility functions
----------------------------
 
function h.overwrite()
-- this is a generic utility function that collects args from the invoke call & the parent template.
-- normally, you merge args with parent template overwriting the invoke call, but
-- since we'll be putting markup/formatting into our invoke call,
-- we actually want to overwrite what the user sent.
local f = mw.getCurrentFrame()
local origArgs = f.args
local parentArgs = f:getParent().args
 
local args = {}
for k, v in pairs(parentArgs) do
v = mw.text.trim(v)
if v ~= '' then
args[k] = v
end
end
for k, v in pairs(origArgs) do
v = mw.text.trim(tostring(v))
if v ~= '' then
args[k] = v
end
end
return args
end
 
-- generic utility functions
-- these would normally be provided by other modules, but to make installation easy
-- I'm including everything here
 
function h.split(text, pattern, plain)
if not text then
return {}
end
local ret = {}
for m in h.gsplit(text, pattern, plain) do
ret[#ret+1] = m
end
return ret
end
 
function h.gsplit( text, pattern, plain )
if not pattern then pattern = ',' end
if not plain then
pattern = '%s*' .. pattern .. '%s*'
end
local s, l = 1, text:len()
return function ()
if s then
local e, n = text:find( pattern, s, plain )
local ret
if not e then
ret = text:sub( s )
s = nil
elseif n < e then
-- Empty separator!
ret = text:sub( s, e )
if e < l then
s = e + 1
else
s = nil
end
else
ret = e > s and text:sub( s, e - 1 ) or ''
s = n + 1
end
return ret
end
end, nil, nil
end
 
function h.escape(s)
s = s:gsub(' ', '')
:gsub('"', '')
:gsub("'", '')
:gsub("%?", '')
:gsub("%%", '')
:gsub("%[", '')
:gsub("%]", '')
:gsub("{", '')
:gsub("}", '')
:gsub("!", '')
return s
end
 
 
-- normally I would make these constants at the top of the file
-- but I don't want to mistake them with user-set constants
h.boolFalse = { ['false'] = true, ['no'] = true, [''] = true, ['0'] = true, ['nil'] = true }
 
function h.castBool(x)
if not x then return false end
return not h.boolFalse[tostring(x):lower()]
end
 
h.htmlEntities = {
table = {
container = 'table',
titleOuter = 'tr',
titleInner = 'th',
section = '',
sectionTitle = 'th',
sectionTabsOuter = 'tr',
sectionTabs = 'td',
row = 'tr',
label = 'th',
cell = 'td',
},
div = {
container = 'div',
titleOuter = 'div',
titleInner = 'div',
section = 'div',
sectionTitle = 'div',
sectionTabsOuter = 'div',
sectionTabs = 'div',
row = 'div',
label = 'div',
cell = 'div',
}
}
 
function h.getTag(key)
-- try not to totally fail here
return h.htmlEntities[h.entityType or 'div'][key]
end
 
return p

Текущая версия от 19:06, 18 октября 2025

-- DRUID Infobox. -- source: https://support.wiki.gg/wiki/Module:Infobox -- license: CC BY-SA 4.0

-- version 0.1.7


-- User settings, you can modify these


-- if you want to not always use divs in your wiki (as opposed to tables), you can change this default -- just remember to change it back each time you update from the main "branch" on the support wiki! -- you can also control it per infobox with `|useDivs=yes` or `|useDivs=no` local USE_DIVS = false -- `false` or `true`

-- default value to show if a param is missing in some but not all tabs. -- set to `nil` (not in quotes) to remove such rows altogether in the tabs where they're missing local TABBED_NONEXIST = nil -- `` or `nil` or `'N/A'` etc. Don't put nil in quotes. local SKIP_SECTIONS_DEFAULT = 2


-- Do not modify anything below this line unless you know what you're doing


local h = {} local p = {} local hooks = {}

function p.arraymap(frame) -- a lua implementation of Page Forms' arraymap local args = h.overwrite() local items = h.split(args[1], args[2] or ',') for i, item in ipairs(items) do items[i] = args[4]:gsub(args[3], item) end return table.concat(items, args[5] or ',') end

function p.preprocess(frame)

 local rawText = frame.args[1] or frame:getParent().args[1]
 return frame:preprocess(rawText)

end

function p.main(frame) h.registerHooks() h.increment() local args = h.overwrite() local sep = args.sep or ',' h.castArgs(args, sep) local skip = tonumber(args.skipSections) or SKIP_SECTIONS_DEFAULT h.skipSections = skip h.printCount = 0 if h.castBool(args.setmainimage or 'yes') then h.setMainImage(args.images[1]) end -- suggest to use HIDDENCAT here; will be used for maintenance & gadget imports local output = h.makeInfobox(args, sep) local finalOutput = tostring(output) return frame:preprocess(finalOutput) end

function h.registerHooks() if not mw.title.new('Module:Infobox/Hooks').exists then return end hooks = require('Module:Infobox/Hooks') end

function h.runHook(key, ...) if hooks[key] then hooks[key](...) end end

function h.increment() -- optional use of VariablesLua for better compatibility local VariablesLua = mw.ext.VariablesLua if VariablesLua == nil then local res -- try to fall back to normal Variables res, h.counter = pcall( function() return mw.getCurrentFrame():callParserFunction('#var', {'DRUID_INFOBOX_ID', 0}) + 1 end ) if res then mw.getCurrentFrame():callParserFunction('#vardefine', {'DRUID_INFOBOX_ID', h.counter}) else -- else use a random number so at least there's some unique id h.counter = math.random(100000000000000000) -- random integer end else h.counter = VariablesLua.var('DRUID_INFOBOX_ID', 0) + 1 VariablesLua.vardefine('DRUID_INFOBOX_ID', h.counter) end end

function h.castArgs(args, sep) h.runHook('onCastArgsStart', args, sep, args.kind) args.tabs = h.split(args.tabs or args.image_labels, sep) args.images = h.getImages(args, sep) args.sections = h.split(args.sections, sep) for _, section in ipairs(args.sections) do if h.castBool(args[section .. '_isdata']) then args[section .. 'Data'] = args[section] args[section] = section .. 'Data' args[section .. 'Data_nolabel'] = 'true' -- will be cast later end args[section] = h.split(args[section], sep) args[section .. '_tabs'] = h.split(args[section .. '_tabs'], sep) if #args.tabs > 0 and #args[section .. '_tabs'] > 0 then error(('You cannot specify |tabs= and |%s= at the same time, please pick one'):format(section .. '_tabs')) end end if args.useDivs then USE_DIVS = h.castBool(args.useDivs) end -- this would be in the outer scope, but we're hiding it h.entityType = USE_DIVS and 'div' or 'table' -- key of h.htmlEntities h.runHook('onCastArgsEnd', args, sep, args.kind) end

function h.getImages(args, sep) if args.image and not args.images then args.images = args.image end if args.images then return h.split(args.images, sep) end if not args.tabs then return {} end local ret = {} for _, key in ipairs(args.tabs) do if args[key .. '_image'] then ret[#ret+1] = args[key .. '_image'] end end return ret end

function h.setMainImage(file) if h.counter > 1 then return end if not file then return end local fileText = file:gsub('.-:', ) fileText = fileText:gsub('^([^|%]]+).*', '%1') -- setmainimage is guaranteed to exist on wiki.gg but may not exist on other wikis -- it's not a crucial piece of functionality so we'll fail silently if it doesn't exist pcall(function() mw.getCurrentFrame():callParserFunction{ name = '#setmainimage', args = { fileText }, } end) end

function h.makeInfobox(args, sep) local out = mw.html.create(h.getTag('container')) :addClass('druid-infobox') :addClass('druid-container') :addClass('noexcerpt') :addClass(args.class) -- warning: class can be nil, don't concat anything :attr('id', args.id or ('druid-container-' .. h.counter))

if args.style then out:cssText(args.style) end

h.runHook('onMakeOutputStart', out, args) if args.kind then out:addClass('druid-container-' .. h.escape(args.kind)) end h.printTitle(out, args) h.printImages(out, args.images, args) for _, section in ipairs(args.sections) do local cols = args[section .. '_columns'] local makeSection = cols and h.makeGridSection or h.makeSection local secNode = makeSection(section, args[section], args, tonumber(cols)) if secNode then out:node(secNode) end end h.runHook('onMakeOutputEnd', out, args) return out end

function h.printTitle(out, args) local tabs = args.tabs if not tabs or #tabs == 0 then h.printSimpleTitle(out, args) return end if not h.hasComplexData('title', tabs, args) then h.printSimpleTitle(out, args) return end local node = h.printTitleWrapper(out) h.printTabbedDataItem(node, 'title', tabs, args) end

function h.printSimpleTitle(out, args) if args.title then local node = h.printTitleWrapper(out) node:wikitext("" .. args.title .. "") end end

function h.printTitleWrapper(out) return out:tag(h.getTag('titleOuter')) :tag(h.getTag('titleInner')) :addClass('druid-title') :attr('colspan', 2) end

function h.printTabbedDataItem(node, item, tabs, args) -- hasData isn't used in the title case but we will need to track this -- when we're printing section data later on -- so we'll just track it always local hasData = false for i, label in ipairs(tabs) do local div = node:tag('div') :addClass('druid-toggleable-data') :addClass('druid-toggleable') :attr('data-druid', h.counter .. '-' .. i) :attr('data-druid-tab-key', label) if h.getTabbedContent(args, label, item) then hasData = true div:wikitext('\n\n' .. h.getTabbedContent(args, label, item)) div:addClass('druid-toggleable-data-nonempty') else div:addClass('druid-toggleable-data-empty') end

if i == 1 then div:addClass('focused') end end return hasData end

function h.printImages(out, images, args) if #images == 0 and #args.tabs == 0 then return end -- burden is on the user to format this as an image. this should be done in the infobox template, -- with something like |image= local td = out:tag(h.getTag('section')) :addClass('druid-section-container') :tag(h.getTag('cell')) :attr('colspan', 2) local tabs = args.tabs local tabTexts = h.getImageTabTexts(tabs, images, args) h.printTabs(td, tabs, tabTexts, false, args) if #images == 0 then return end if #images == 1 then td:addClass('druid-main-image') :wikitext(images[1]) if args.caption then td:tag('div') :addClass('druid-main-image-caption') :wikitext(args.caption) end return end td:addClass('druid-main-images') local imagesContainer = td:tag('div') :addClass('druid-main-images-files') for i, image in ipairs(images) do local container = imagesContainer:tag('div') :addClass('druid-main-images-file') :addClass('druid-toggleable') :attr('data-druid', h.counter .. '-' .. i) :wikitext(image) :attr('data-druid-tab-key', tabs[i]) local labelText if tabs[i] then labelText = args[tabs[i] .. '_label'] or tabs[i] else labelText = 'Image ' .. i end if args[labelText .. '_caption'] then container:tag('div') :addClass('druid-main-images-caption') :wikitext(args[labelText .. '_caption']) end if i == 1 then container:addClass('focused') end end end

function h.getImageTabTexts(tabs, images, args) if #tabs == 0 and #images <= 1 then return {} end local texts = {} local i = 1 while images[i] or tabs[i] do if tabs[i] then texts[i] = args[tabs[i] .. '_label'] or tabs[i] else texts[i] = 'Image ' .. i end i = i + 1 end return texts end

function h.printTabs(td, tabs, texts, isSection, args) if #texts == 0 then return end local container = td:tag('div') :addClass('druid-main-images-labels') :addClass('druid-tabs') if isSection then container:addClass('druid-section-tabs') end for i, item in ipairs(tabs) do local label = container:tag('div') :addClass('druid-main-images-label') :addClass('druid-tab') :addClass('druid-toggleable') :attr('data-druid', h.counter .. '-' .. i) :wikitext(texts[i]) :attr('data-druid-tab-key', item) if isSection then label:addClass('druid-section-tab') else label:addClass('druid-title-tab') end if i == 1 then label:addClass('focused') end -- this can be null, don't concat anything here label:addClass(args[item .. '_class']) end end

function h.makeGridSection(section, sectionFields, args, numCols) local numItems = h.countItems(sectionFields, section, args) if numItems == 0 then return end local node = mw.html.create(h.getTag('section')) :addClass('druid-section-container') h.printSectionHeader(node, section, args) h.printSectionTabs(node, section, args) local tr = node:tag(h.getTag('row')) :attr('data-druid-section-row', h.escape(section)) if args[section .. '_collapsed'] then tr:addClass('druid-collapsed') end local grid = tr:tag(h.getTag('cell')) :attr('colspan', 2) :addClass('druid-grid-section') :addClass('druid-grid-section-' .. h.escape(section)) :addClass(args[section .. '_class']) -- warning: class can be nil, don't concat anything :tag('div') :addClass('druid-grid') local row, col, i = 1, 1, 1 local sizeOfLastRow = numItems % numCols local lcm = h.getNumGridCols(numItems, sizeOfLastRow, numCols) grid:css('grid-template-columns', ('repeat(%s, 1fr)'):format(lcm)) local size = lcm / numCols for _, item in ipairs(sectionFields) do local node = mw.html.create('div') local shouldPrint = h.printData(node, item, section, args) if shouldPrint then if i == numItems - sizeOfLastRow + 1 then size = lcm / sizeOfLastRow end i = i + 1 local gStart = (col - 1) * size + 1 local gEnd = (col) * size + 1 local itemContainer = grid:tag('div') :addClass('druid-grid-item') :addClass('druid-grid-item-' .. h.escape(item)) :addClass(args[item .. '_class']) -- warning: class can be nil, don't concat anything :css('grid-column', ('%s / %s'):format(gStart, gEnd)) :css('grid-row', row) if not h.castBool(args[item .. '_nolabel']) then h.printLabel(itemContainer:tag('div'), item, args) end itemContainer:node(node) if col == numCols then row = row + 1 col = 1 else col = col + 1 end end end return node end

function h.makeSection(section, sectionFields, args) if section == then return end -- bruteforce fix for trailing commas local shouldPrint = false local container = mw.html.create(h.getTag('section')) :addClass('druid-section-container') :addClass(args[section .. '_class']) -- warning: class can be nil, don't concat anything h.printSectionHeader(container, section, args) h.printSectionTabs(container, section, args) for _, item in ipairs(sectionFields) do local node = mw.html.create(h.getTag('cell')) local shouldPrintItem = h.printData(node, item, section, args) if shouldPrintItem then h.printCount = h.printCount + 1 shouldPrint = true local tr = container:tag(h.getTag('row')) :addClass('druid-row') :addClass('druid-row-' .. h.escape(item)) :addClass(args[item .. '_class']) -- warning: class can be nil, don't concat anything :attr('data-druid-section-row', h.escape(section)) if args[section .. '_collapsed'] then tr:addClass('druid-collapsed') end if h.castBool(args[item .. '_wide']) or h.castBool(args[item .. '_nolabel']) then node :attr('colspan', 2) :addClass('druid-data-wide') else h.printLabel(tr:tag(h.getTag('label')), item, args) end tr:node(node) end end if not shouldPrint then return nil end return container end

function h.countItems(sectionFields, section, args) local numItems = 0 for _, v in ipairs(sectionFields) do -- we aren't actually printing here, but we're finding out if we should print anything -- because we need the count of columns before we print anything in grid data if h.printData(mw.html.create(), v, section, args) then numItems = numItems + 1 end end return numItems end

function h.getNumGridCols(numItems, sizeOfLastRow, numCols) if not numCols then return numItems, 1 end if numItems < numCols then return numItems, 1 end if sizeOfLastRow == 0 then return numCols, 1 end local a, b = sizeOfLastRow, numCols while b ~= 0 do a, b = b, a % b end local lcm = sizeOfLastRow * numCols / a return lcm end

function h.printLabel(node, item, args) local text = args[item .. '_display'] or args[item .. '_label'] or item return node :addClass('druid-label') :addClass('druid-label-' .. h.escape(item)) :wikitext("" .. text .. "") end

function h.printData(node, item, section, args) -- prints data to the node -- and also returns whether the item is nonempty or not local hasData = false local sectionTabs = args[section .. '_tabs'] local tabs = args.tabs if sectionTabs and #sectionTabs > 0 then tabs = sectionTabs end if not tabs or #tabs == 0 then return h.printSimpleData(node, item, args) end if not h.hasComplexData(item, tabs, args) then return h.printSimpleData(node, item, args) end hasData = hasData or h.printTabbedDataItem(node, item, tabs, args) if hasData then node:addClass('druid-data') end return hasData end

function h.getTabbedContent(args, label, item) return args[label .. '_' .. item] or args[item] or TABBED_NONEXIST end

function h.printSimpleData(node, item, args) if args[item] and type(args[item]) ~= 'string' then error(("Invalid use of field %s as both a section and a data value"):format(item)) end if not args[item] then return false end node:addClass('druid-data') :addClass('druid-data-' .. h.escape(item)) :addClass('druid-data-nonempty') :wikitext("\n\n" .. args[item] .. "") return true end

function h.hasComplexData(item, tabs, args) for _, v in ipairs(tabs) do if args[v .. '_' .. item] then return true end end return false end

function h.printSectionHeader(node, section, args) local fields = args[section] or {} local hasContent = args[section .. '_columns'] and (h.countItems(fields, section, args) > 0) or (function() for _, item in ipairs(fields) do if h.printData(mw.html.create(), item, section, args) then return true end end return false end)() if not hasContent then return end

if h.castBool(args[section .. '_nolabel']) then return end

h.printCount = h.printCount + 1

if h.printCount <= h.skipSections then return end

local tr = node:tag(h.getTag('row')) :attr('data-druid-section', h.escape(section)) :addClass('data-druid-section') local th = tr:tag(h.getTag('sectionTitle')) :attr('colspan', 2) :addClass('druid-section') :addClass('druid-section-' .. h.escape(section)) if args[section .. '_collapsible'] or args[section .. '_collapsed'] then tr:addClass('druid-collapsible') if args[section .. '_collapsed'] then tr:addClass('druid-collapsible-collapsed') end end

local emptySections = {} for _, label in ipairs(args.tabs) do local hasLabel = false for _, item in ipairs(args[section] or {}) do if h.getTabbedContent(args, label, item) then hasLabel = true end end if not hasLabel then emptySections[label] = true end end if not next(emptySections) then th:wikitext("" .. (args[section .. '_label'] or section) .. "") return end for i, label in ipairs(args.tabs) do local div = th:tag('div') :addClass('druid-toggleable-heading') :addClass('druid-toggleable') :attr('data-druid', h.counter .. '-' .. i) :wikitext("" .. (args[section .. '_label'] or section) .. "") -- we are going to print the section content even in empty nodes -- for compatibility with browsers without :has, where hiding empty rows won't happen if emptySections[label] then div:addClass('druid-toggleable-heading-empty') end if i == 1 then div:addClass('focused') end end end

function h.printSectionTabs(node, section, args) local tabs = args[section .. '_tabs'] if not tabs or #tabs == 0 then return end local tr = node:tag(h.getTag('sectionTabsOuter')) :attr('data-druid-section', h.escape(section)) :addClass('data-druid-section') local th = tr:tag(h.getTag('sectionTabs')) :attr('colspan', 2) :addClass('druid-section-tabs') :addClass('druid-section-tabs-' .. h.escape(section)) local texts = {} for i, item in ipairs(tabs) do texts[i] = args[item .. '_label'] or item end h.printTabs(th, tabs, texts, true, args) end


-- general utility functions


function h.overwrite() -- this is a generic utility function that collects args from the invoke call & the parent template. -- normally, you merge args with parent template overwriting the invoke call, but -- since we'll be putting markup/formatting into our invoke call, -- we actually want to overwrite what the user sent. local f = mw.getCurrentFrame() local origArgs = f.args local parentArgs = f:getParent().args

local args = {}

for k, v in pairs(parentArgs) do v = mw.text.trim(v) if v ~= then args[k] = v end end

for k, v in pairs(origArgs) do v = mw.text.trim(tostring(v)) if v ~= then args[k] = v end end

return args end

-- generic utility functions -- these would normally be provided by other modules, but to make installation easy -- I'm including everything here

function h.split(text, pattern, plain) if not text then return {} end local ret = {} for m in h.gsplit(text, pattern, plain) do ret[#ret+1] = m end return ret end

function h.gsplit( text, pattern, plain ) if not pattern then pattern = ',' end if not plain then pattern = '%s*' .. pattern .. '%s*' end local s, l = 1, text:len() return function () if s then local e, n = text:find( pattern, s, plain ) local ret if not e then ret = text:sub( s ) s = nil elseif n < e then -- Empty separator! ret = text:sub( s, e ) if e < l then s = e + 1 else s = nil end else ret = e > s and text:sub( s, e - 1 ) or s = n + 1 end return ret end end, nil, nil end

function h.escape(s) s = s:gsub(' ', ) :gsub('"', ) :gsub("'", ) :gsub("%?", ) :gsub("%%", ) :gsub("%[", ) :gsub("%]", ) :gsub("{", ) :gsub("}", ) :gsub("!", ) return s end


-- normally I would make these constants at the top of the file -- but I don't want to mistake them with user-set constants h.boolFalse = { ['false'] = true, ['no'] = true, [] = true, ['0'] = true, ['nil'] = true }

function h.castBool(x) if not x then return false end return not h.boolFalse[tostring(x):lower()] end

h.htmlEntities = { table = { container = 'table', titleOuter = 'tr', titleInner = 'th', section = , sectionTitle = 'th', sectionTabsOuter = 'tr', sectionTabs = 'td', row = 'tr', label = 'th', cell = 'td', }, div = { container = 'div', titleOuter = 'div', titleInner = 'div', section = 'div', sectionTitle = 'div', sectionTabsOuter = 'div', sectionTabs = 'div', row = 'div', label = 'div', cell = 'div', } }

function h.getTag(key) -- try not to totally fail here return h.htmlEntities[h.entityType or 'div'][key] end

return p