Module:Infobox
![]() | This Lua module is used on approximately 4,290,000 pages, or roughly 45692% of all pages. To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Consider discussing changes on the talk page before implementing them. |
![]() |
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
Lua error in Module:Lua_banner at line 113: attempt to index field 'edit' (a nil value). Lua error in Module:TNT at line 159: Missing JsonConfig extension; Cannot load https://commons.wikimedia.org/wiki/Data:I18n/Uses TemplateStyles.tab.
Module:Infobox is a module that implements the {{Infobox}} template. Please see the template page for usage instructions.
Tracking categories
- Category:Pages using infobox templates with ignored data cells (2)
- Category:Articles using infobox templates with no data rows (3)
- Category:Pages using embedded infobox templates with the title parameter (3)
local counter
local TABBED_NONEXIST = '' -- wait until ~May 2024 to make this nil
local h = {}
local p = {}
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)
return frame:preprocess(frame.args[1] or frame:getParent().args[1])
end
function p.main(frame)
h.increment()
local args = h.overwrite()
local sep = args.sep or ','
h.castArgs(args, sep)
h.setMainImage(args.images[1])
return h.makeInfobox(args, sep)
end
function h.increment()
counter = mw.getCurrentFrame():callParserFunction('#var', {'DRUID_INFOBOX_ID', 0}) + 1
mw.getCurrentFrame():callParserFunction('#vardefine', {'DRUID_INFOBOX_ID', counter})
end
function h.castArgs(args, sep)
if args.image and not args.images then
args.images = args.image
end
args.images = h.split(args.images, sep)
args.image_labels = h.split(args.image_labels, sep)
args.sections = h.split(args.sections, sep)
for _, section in ipairs(args.sections) do
args[section] = h.split(args[section], sep)
end
end
function h.setMainImage(file)
if not file then return end
mw.getCurrentFrame():callParserFunction{
name = '#setmainimage',
args = { file:gsub('File:', '') },
}
end
function h.makeInfobox(args, sep)
local out = mw.html.create('table')
:addClass('druid-infobox')
:addClass('druid-container')
:attr('id', 'druid-container-' .. counter)
if args.kind then out:addClass('druid-container-' .. h.escape(args.kind)) end
if args.title then
out:tag('tr')
:tag('th')
:addClass('druid-title')
:attr('colspan', 2)
:wikitext(args.title)
end
h.printImages(out, args.images, args)
for _, section in ipairs(args.sections) do
-- cannot begin tagging here because we don't know if any applicable args are present
local cols = args[section .. '_columns']
local makeSection = cols and h.makeGridSection or h.makeSection
out:node(makeSection(section, args[section], args, tonumber(cols)))
end
return out
end
function h.printImages(out, images, args)
if #images == 0 then return end
local labels = args.image_labels
-- burden is on the user to format this as an image. this should be done in the infobox template,
-- with something like |image={{#if:{{{image|}}}|[[File:{{{image|}}}{{!}}300px{{!}}link=]]}}
local td = out:tag('tr')
:tag('td')
:attr('colspan', 2)
if #images == 1 then
td:addClass('druid-main-image')
:wikitext(images[1])
return
end
td:addClass('druid-main-images')
local labelsContainer = td:tag('div')
:addClass('druid-main-images-labels')
local imagesContainer = td:tag('div')
:addClass('druid-main-images-files')
for i, item in ipairs(images) do
local labelText = labels[i] or ('[[Category:Infoboxes missing image labels]]Image ' .. i)
local label = labelsContainer:tag('div')
:addClass('druid-main-images-label')
:addClass('druid-toggleable')
:attr('data-druid', counter .. '-' .. i)
:wikitext(labelText)
local container = imagesContainer:tag('div')
:addClass('druid-main-images-file')
:addClass('druid-toggleable')
:attr('data-druid', counter .. '-' .. i)
:wikitext(item)
if args[labelText .. '_caption'] then
container:tag('div')
:addClass('druid-main-images-caption')
:wikitext(args[labelText .. '_caption'])
end
if i == 1 then
label:addClass('focused')
container:addClass('focused')
end
end
end
function h.makeGridSection(section, sectionFields, args, cols)
local shouldPrint = false
local node = mw.html.create()
h.printSectionHeader(node, section, args)
local tr = node:tag('tr')
:attr('data-druid-section-row', h.escape(section))
if args[section .. '_collapsed'] then
tr:addClass('druid-collapsed')
end
local grid = tr:tag('td')
:attr('colspan', 2)
:addClass('druid-grid-section')
:addClass('druid-grid-section-' .. h.escape(section))
:tag('div')
:addClass('druid-grid')
:css('grid-template-columns', ('repeat(%s, 1fr)'):format(cols))
local row = 1
local col = 1
local itemContainer
for _, item in ipairs(sectionFields) do
if args[item] then
shouldPrint = true
itemContainer = grid:tag('div')
:addClass('druid-grid-item')
:addClass('druid-grid-item-' .. h.escape(item))
:css('grid-column', col)
:css('grid-row', row)
if not args[item .. '_nolabel'] then
h.printLabel(itemContainer:tag('div'), item, args)
end
h.printData(itemContainer:tag('div'), item, args)
if col == cols then
row = row + 1
col = 1
else
col = col + 1
end
end
end
if not shouldPrint then return nil end
itemContainer:css('grid-column', ('%s / -1'):format(col - 1))
return node
end
function h.makeSection(section, sectionFields, args)
local shouldPrint = false
local node = mw.html.create()
h.printSectionHeader(node, section, args)
for _, item in ipairs(sectionFields) do
if h.shouldPrint(item, args) then
shouldPrint = true
local tr = node:tag('tr')
:addClass('druid-row')
:addClass('druid-row-' .. h.escape(item))
:attr('data-druid-section-row', h.escape(section))
if args[section .. '_collapsed'] then
tr:addClass('druid-collapsed')
end
if args[item .. '_wide'] or args[item .. '_nolabel'] then
local td = h.printData(tr:tag('td'), item, args)
td
:attr('colspan', 2)
:addClass('druid-data-wide')
else
h.printLabel(tr:tag('th'), item, args)
h.printData(tr:tag('td'), item, args)
end
end
end
if not shouldPrint then return nil end
return node
end
function h.shouldPrint(item, args)
if args[item] then return true end
for _, key in ipairs(args.image_labels) do
if args[key .. '_' .. item] then
return true
end
end
return false
end
function h.printLabel(node, item, args)
return node
:addClass('druid-label')
:addClass('druid-label-' .. h.escape(item))
:wikitext(args[item .. '_display'] or args[item .. '_label'] or item)
end
function h.printData(node, item, args)
if not args.image_labels or #args.image_labels == 0 then
h.printSimpleData(node, item, args)
return node
end
if not h.hasComplexData(item, args) then
h.printSimpleData(node, item, args)
return node
end
for i, label in ipairs(args.image_labels) do
local div = node:tag('div')
:addClass('druid-toggleable-data')
:addClass('druid-toggleable')
:attr('data-druid', counter .. '-' .. i)
if h.getTabbedContent(args, label, item) then
div:wikitext('\n\n' .. h.getTabbedContent(args, label, item))
else
div:addClass('druid-toggleable-data-empty')
end
if i == 1 then div:addClass('focused') end
end
return node
end
function h.getTabbedContent(args, label, item)
return args[label .. '_' .. item] or args[item] or TABBED_NONEXIST
end
function h.printSimpleData(node, item, args)
node:addClass('druid-data')
:addClass('druid-data-' .. h.escape(item))
:wikitext('\n\n' .. args[item])
end
function h.hasComplexData(item, args)
for _, v in ipairs(args.image_labels) do
if args[v .. '_' .. item] then return true end
end
return false
end
function h.printSectionHeader(node, section, args)
if args[section .. '_nolabel'] then return end
local tr = node:tag('tr')
:attr('data-druid-section', h.escape(section))
local th = tr:tag('th')
:attr('colspan', 2)
:addClass('druid-section')
:addClass('druid-section-' .. h.escape(section))
if args[section .. '_collapsible'] then
tr:addClass('druid-collapsible')
if args[section .. '_collapsed'] then
tr:addClass('druid-collapsible-collapsed')
end
end
local emptySections = {}
for _, label in ipairs(args.image_labels) 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.image_labels) do
local div = th:tag('div')
:addClass('druid-toggleable-heading')
:addClass('druid-toggleable')
:attr('data-druid', 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.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
return p