Toggle menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:InfoboxNeue: Difference between revisions

From TCG Card Shop Simulator
No edit summary
Tag: Reverted
No edit summary
Tag: Manual revert
 
Line 12: Line 12:


metatable.__tostring = function ( self )
metatable.__tostring = function ( self )
return tostring( self:renderInfobox() )
return tostring( self:renderInfobox() )
end
end



--- Wrapper function for Module:i18n.translate
--- Wrapper function for Module:i18n.translate
---
--- @param key string The translation key
--- @return string If the key was not found, the key is returned
local function t( key )
local function t( key )
return i18n:translate( key )
return i18n:translate( key )
end
end



--- Helper function to restore underscore from space
--- Helper function to restore underscore from space
--- so that it does not screw up the external link wikitext syntax
--- For some reason SMW property converts underscore into space
--- mw.uri.encode can't be used on full URL
local function restoreUnderscore( s )
local function restoreUnderscore( s )
return s:gsub( ' ', '%%5F' )
return s:gsub( ' ', '%%5F' )
end
end


--- Helper function to format string to number with separators
--- Helper function to format string to number with separators
--- It is usually use to re-format raw number from SMW into more readable format
local function formatNumber( s )
local function formatNumber( s )
local lang = mw.getContentLanguage()
local lang = mw.getContentLanguage()
if s == nil then
if s == nil then
return
return
end
end


if type( s ) ~= 'number' then
if type( s ) ~= 'number' then
s = tonumber( s )
s = tonumber( s )
end
end


if type( s ) == 'number' then
if type( s ) == 'number' then
return lang:formatNum( s )
return lang:formatNum( s )
end
end


return s
return s
end
end


-- TODO: Perhaps we should turn this into another module
--- Updated function to manually render <details> and <summary>
local function getDetailsHTML( data, frame )
local function getDetailsHTML( data, frame )
local summary = mw.html.create( 'summary' )
local summary = frame:extensionTag {
name = 'summary',
:addClass( data.summary.class )
:wikitext( data.summary.content )
content = data.summary.content,
args = {

class = data.summary.class
local details = mw.html.create( 'details' )
}
:addClass( data.details.class )
}

local details = frame:extensionTag {
if data.details.open then
details:attr( 'open', 'open' )
name = 'details',
content = summary .. data.details.content,
end
args = {

details:node( summary )
class = data.details.class,
open = true
:wikitext( data.details.content )
}

}
return tostring( details )
return details
end
end



--- Put table values into a comma-separated list
--- Put table values into a comma-separated list
---
--- @param data table
--- @return string
function methodtable.tableToCommaList( data )
function methodtable.tableToCommaList( data )
if type( data ) == 'table' then
if type( data ) == 'table' then
return table.concat( data, ', ' )
return table.concat( data, ', ' )
else
else
return data
return data
end
end
end
end


--- Show range if value1 and value2 are different
--- Show range if value1 and value2 are different
---
--- @param s1 string|nil
--- @param s2 string|nil
--- @return string|nil
function methodtable.formatRange( s1, s2, formatNum )
function methodtable.formatRange( s1, s2, formatNum )
if s1 == nil and s2 == nil then
if s1 == nil and s2 == nil then
return
return
end
end


formatNum = formatNum or false
formatNum = formatNum or false


if formatNum then
if formatNum then
if s1 then
if s1 then
s1 = formatNumber( s1 )
s1 = formatNumber( s1 )
end
end
if s2 then
if s2 then
s2 = formatNumber( s2 )
s2 = formatNumber( s2 )
end
end
end
end


if s1 and s2 and s1 ~= s2 then
if s1 and s2 and s1 ~= s2 then
return s1 .. ' – ' .. s2
return s1 .. ' – ' .. s2
end
end


return s1 or s2
return s1 or s2
end
end


--- Append unit to the value if exists
--- Append unit to the value if exists
---
--- @param s string
--- @param unit string
--- @return string|nil
function methodtable.addUnitIfExists( s, unit )
function methodtable.addUnitIfExists( s, unit )
if s == nil then
if s == nil then
return
return
end
end


return s .. ' ' .. unit
return s .. ' ' .. unit
end
end


--- Shortcut to return the HTML of the infobox message component as string
--- Shortcut to return the HTML of the infobox message component as string
---
--- @param data table {title, desc)
--- @return string html
function methodtable.renderMessage( self, data, noInsert )
function methodtable.renderMessage( self, data, noInsert )
checkType( 'Module:InfoboxNeue.renderMessage', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderMessage', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderMessage', 2, data, 'table' )
checkType( 'Module:InfoboxNeue.renderMessage', 2, data, 'table' )
checkType( 'Module:InfoboxNeue.renderMessage', 3, noInsert, 'boolean', true )
checkType( 'Module:InfoboxNeue.renderMessage', 3, noInsert, 'boolean', true )


noInsert = noInsert or false
noInsert = noInsert or false


local item = self:renderSection( { content = self:renderItem( { data = data.title, desc = data.desc } ) }, noInsert )
local item = self:renderSection( { content = self:renderItem( { data = data.title, desc = data.desc } ) }, noInsert )


if not noInsert then
if not noInsert then
table.insert( self.entries, item )
table.insert( self.entries, item )
end
end


return item
return item
end
end


--- Return the HTML of the infobox image component as string
--- Return the HTML of the infobox image component as string
---
--- @param filename string
--- @return string html
function methodtable.renderImage( self, filename )
function methodtable.renderImage( self, filename )
checkType( 'Module:InfoboxNeue.renderImage', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderImage', 1, self, 'table' )


local hasPlaceholderImage = false
local hasPlaceholderImage = false


if type( filename ) ~= 'string' and self.config.displayPlaceholder == true then
if type( filename ) ~= 'string' and self.config.displayPlaceholder == true then
hasPlaceholderImage = true
hasPlaceholderImage = true
filename = self.config.placeholderImage
filename = self.config.placeholderImage
-- Add tracking category for infoboxes using placeholder image
table.insert( self.categories,
table.insert( self.categories,
string.format( '[[Category:%s]]', t( 'category_infobox_using_placeholder_image' ) )
string.format( '[[Category:%s]]', t( 'category_infobox_using_placeholder_image' ) )
)
)
end
end


if type( filename ) ~= 'string' then
if type( filename ) ~= 'string' then
return ''
return ''
end
end


local parts = mw.text.split( filename, ':', true )
local parts = mw.text.split( filename, ':', true )
if #parts > 1 then
if #parts > 1 then
table.remove( parts, 1 )
table.remove( parts, 1 )
filename = table.concat( parts, ':' )
filename = table.concat( parts, ':' )
end
end


local html = mw.html.create( 'div' )
local html = mw.html.create( 'div' )
:addClass( 'infobox__image' )
:addClass( 'infobox__image' )
:wikitext( string.format( '[[File:%s|400px]]', filename ) )
:wikitext( string.format( '[[File:%s|400px]]', filename ) )


if hasPlaceholderImage == true then
if hasPlaceholderImage == true then
local icon = mw.html.create( 'span' ):addClass( 'citizen-ui-icon mw-ui-icon-wikimedia-upload' )
local icon = mw.html.create( 'span' ):addClass( 'citizen-ui-icon mw-ui-icon-wikimedia-upload' )
-- TODO: Point the Upload link to a specific file name
html:tag( 'div' ):addClass( 'infobox__image-upload' )
html:tag( 'div' ):addClass( 'infobox__image-upload' )
:wikitext( string.format( '[[%s|%s]]', 'Special:UploadWizard',
:wikitext( string.format( '[[%s|%s]]', 'Special:UploadWizard',
tostring( icon ) .. t( 'label_upload_image' ) ) )
tostring( icon ) .. t( 'label_upload_image' ) ) )
end
end


local item = tostring( html )
local item = tostring( html )


table.insert( self.entries, item )
table.insert( self.entries, item )


return item
return item
end
end


--- Return the HTML of the infobox indicator component as string
--- Return the HTML of the infobox indicator component as string
---
--- @param data table {data, class, color, nopadding)
--- @return string html
function methodtable.renderIndicator( self, data )
function methodtable.renderIndicator( self, data )
checkType( 'Module:InfoboxNeue.renderIndicator', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderIndicator', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderIndicator', 2, data, 'table' )
checkType( 'Module:InfoboxNeue.renderIndicator', 2, data, 'table' )


if data == nil or data[ 'data' ] == nil or data[ 'data' ] == '' then return '' end
if data == nil or data[ 'data' ] == nil or data[ 'data' ] == '' then return '' end


local html = mw.html.create( 'div' ):addClass( 'infobox__indicators' )
local html = mw.html.create( 'div' ):addClass( 'infobox__indicators' )


local htmlClasses = {
local htmlClasses = {
'infobox__indicator'
'infobox__indicator'
}
}


if data[ 'class' ] then
if data[ 'class' ] then
table.insert( htmlClasses, data[ 'class' ] )
table.insert( htmlClasses, data[ 'class' ] )
end
end


if data[ 'color' ] then
if data[ 'color' ] then
table.insert( htmlClasses, 'infobox__indicator--' .. data[ 'color' ] )
table.insert( htmlClasses, 'infobox__indicator--' .. data[ 'color' ] )
end
end


if data[ 'nopadding' ] == true then
if data[ 'nopadding' ] == true then
table.insert( htmlClasses, 'infobox__indicator--nopadding' )
table.insert( htmlClasses, 'infobox__indicator--nopadding' )
end
end


html:wikitext(
html:wikitext(
self:renderItem(
self:renderItem(
{
{
[ 'data' ] = data[ 'data' ],
[ 'data' ] = data[ 'data' ],
[ 'class' ] = table.concat( htmlClasses, ' ' ),
[ 'class' ] = table.concat( htmlClasses, ' ' ),
row = true,
row = true,
spacebetween = true
spacebetween = true
}
}
)
)
)
)


local item = tostring( html )
local item = tostring( html )


table.insert( self.entries, item )
table.insert( self.entries, item )


return item
return item
end
end


--- Return the HTML of the infobox header component as string
--- Remaining methods are unchanged ---
---
--- RenderHeader, RenderFooter, and other helper functions remain exactly the same.
--- @param data table {title, subtitle, badge)
--- Copy and paste the remaining sections of your code here as needed.
--- @return string html
function methodtable.renderHeader( self, data )
checkType( 'Module:InfoboxNeue.renderHeader', 1, self, 'table' )
checkTypeMulti( 'Module:InfoboxNeue.renderHeader', 2, data, { 'table', 'string' } )

if type( data ) == 'string' then
data = {
title = data
}
end

if data == nil or data[ 'title' ] == nil then return '' end

local html = mw.html.create( 'div' ):addClass( 'infobox__header' )

if data[ 'badge' ] then
html:tag( 'div' )
:addClass( 'infobox__item infobox__badge' )
:wikitext( data[ 'badge' ] )
end

local titleItem = mw.html.create( 'div' ):addClass( 'infobox__item' )

titleItem:tag( 'div' )
:addClass( 'infobox__title' )
:wikitext( data[ 'title' ] )

if data[ 'subtitle' ] then
titleItem:tag( 'div' )
-- Subtitle is always data
:addClass( 'infobox__subtitle infobox__data' )
:wikitext( data[ 'subtitle' ] )
end

html:node( titleItem )

local item = tostring( html )

table.insert( self.entries, item )

return item
end

--- Wrap the HTML into an infobox section
---
--- @param data table {title, subtitle, content, border, col, class}
--- @param noInsert boolean whether to insert this section into the internal table table
--- @return string html
function methodtable.renderSection( self, data, noInsert )
checkType( 'Module:InfoboxNeue.renderSection', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderSection', 2, data, 'table' )
checkType( 'Module:InfoboxNeue.renderSection', 3, noInsert, 'boolean', true )

noInsert = noInsert or false

if type( data.content ) == 'table' then
data.content = table.concat( data.content )
end

if data == nil or data[ 'content' ] == nil or data[ 'content' ] == '' then return '' end

local html = mw.html.create( 'div' ):addClass( 'infobox__section' )

if data[ 'title' ] then
local header = html:tag( 'div' ):addClass( 'infobox__sectionHeader' )
header:tag( 'div' )
:addClass( 'infobox__sectionTitle' )
:wikitext( data[ 'title' ] )
if data[ 'subtitle' ] then
header:tag( 'div' )
:addClass( 'infobox__sectionSubtitle' )
:wikitext( data[ 'subtitle' ] )
end
end

local content = html:tag( 'div' )
content:addClass( 'infobox__sectionContent' )
:wikitext( data[ 'content' ] )

if data[ 'border' ] == false then html:addClass( 'infobox__section--noborder' ) end
if data[ 'col' ] then content:addClass( 'infobox__grid--cols-' .. data[ 'col' ] ) end
if data[ 'class' ] then html:addClass( data[ 'class' ] ) end

local item = tostring( html )

if not noInsert then
table.insert( self.entries, item )
end

return item
end

--- Return the HTML of the infobox link button component as string
---
--- @param data table {label, link, page}
--- @return string html
function methodtable.renderLinkButton( self, data )
checkType( 'Module:InfoboxNeue.renderLinkButton', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderLinkButton', 2, data, 'table' )

if data == nil or data[ 'label' ] == nil or (data[ 'link' ] == nil and data[ 'page' ] == nil) then return '' end

--- Render multiple linkButton when link is a table
if type( data[ 'link' ] ) == 'table' then
local htmls = {}

for i, url in ipairs( data[ 'link' ] ) do
table.insert( htmls,
self:renderLinkButton( {
label = string.format( '%s %d', data[ 'label' ], i ),
link = url
} )
)
end

return table.concat( htmls )
end

local html = mw.html.create( 'div' ):addClass( 'infobox__linkButton' )

if data[ 'link' ] then
html:wikitext( string.format( '[%s %s]', restoreUnderscore( data[ 'link' ] ), data[ 'label' ] ) )
elseif data[ 'page' ] then
html:wikitext( string.format( '[[%s|%s]]', data[ 'page' ], data[ 'label' ] ) )
end

return tostring( html )
end

--- Return the HTML of the infobox footer component as string
---
--- @param data table {content, button}
--- @return string html
function methodtable.renderFooter( self, data )
checkType( 'Module:InfoboxNeue.renderFooter', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderFooter', 2, data, 'table' )

if data == nil then return '' end

-- Checks if an input is of type 'table' or 'string' and if it is not empty
local function isNonEmpty( input )
return (type( input ) == 'table' and next( input ) ~= nil) or (type( input ) == 'string' and #input > 0)
end

local hasContent = isNonEmpty( data[ 'content' ] )
local hasButton = isNonEmpty( data[ 'button' ] ) and isNonEmpty( data[ 'button' ][ 'content' ] ) and
isNonEmpty( data[ 'button' ][ 'label' ] )

if not hasContent and not hasButton then return '' end

local html = mw.html.create( 'div' ):addClass( 'infobox__footer' )

if hasContent then
local content = data[ 'content' ]
if type( content ) == 'table' then content = table.concat( content ) end

html:addClass( 'infobox__footer--has-content' )
html:tag( 'div' )
:addClass( 'infobox__section' )
:wikitext( content )
end

if hasButton then
html:addClass( 'infobox__footer--has-button' )
local buttonData = data[ 'button' ]
local button = html:tag( 'div' ):addClass( 'infobox__button' )
local label = button:tag( 'div' ):addClass( 'infobox__buttonLabel' )

if buttonData[ 'icon' ] ~= nil then
label:wikitext( string.format( '[[File:%s|16px|link=]]%s', buttonData[ 'icon' ], buttonData[ 'label' ] ) )
else
label:wikitext( buttonData[ 'label' ] )
end

if buttonData[ 'type' ] == 'link' then
button:tag( 'div' )
:addClass( 'infobox__buttonLink' )
:wikitext( buttonData[ 'content' ] )
elseif buttonData[ 'type' ] == 'popup' then
button:tag( 'div' )
:addClass( 'infobox__buttonCard' )
:wikitext( buttonData[ 'content' ] )
end
end

local item = tostring( html )

table.insert( self.entries, item )

return item
end

--- Return the HTML of the infobox footer button component as string
---
--- @param data table {icon, label, type, content}
--- @return string html
function methodtable.renderFooterButton( self, data )
checkType( 'Module:InfoboxNeue.renderFooterButton', 1, self, 'table' )
checkType( 'Module:InfoboxNeue.renderFooterButton', 2, data, 'table' )

if data == nil then return '' end

return self:renderFooter( { button = data } )
end

--- Return the HTML of the infobox item component as string
---
--- @param data table {label, data, desc, class, tooltip, icon, row, spacebetween, colspan)
--- @param content string|number|nil optional
--- @return string html
function methodtable.renderItem( self, data, content )
checkType( 'Module:InfoboxNeue.renderItem', 1, self, 'table' )
checkTypeMulti( 'Module:InfoboxNeue.renderItem', 2, data, { 'table', 'string' } )
checkTypeMulti( 'Module:InfoboxNeue.renderItem', 3, content, { 'string', 'number', 'nil' } )

-- The arguments are not passed as a table
-- Allows to call this as box:renderItem( 'Label', 'Data' )
if content ~= nil then
data = {
label = data,
data = content
}
end

if data == nil or data[ 'data' ] == nil or data[ 'data' ] == '' then return '' end

if self.config.removeEmpty == true and data[ 'data' ] == self.config.emptyString then
return ''
end

local html = mw.html.create( 'div' ):addClass( 'infobox__item' )

if data[ 'class' ] then html:addClass( data[ 'class' ] ) end
if data[ 'tooltip' ] then html:attr( 'title', data[ 'tooltip' ] ) end
if data[ 'row' ] == true then html:addClass( 'infobox__grid--row' ) end
if data[ 'spacebetween' ] == true then html:addClass( 'infobox__grid--space-between' ) end
if data[ 'colspan' ] then html:addClass( 'infobox__grid--col-span-' .. data[ 'colspan' ] ) end

local textWrapper = html

if data[ 'link' ] then
html:addClass( 'infobox__itemButton' )
html:tag( 'div' )
:addClass( 'infobox__itemButtonLink' )
:wikitext( string.format( '[%s]', data[ 'link' ] ) )
elseif data[ 'page' ] then
html:addClass( 'infobox__itemButton' )
html:tag( 'div' )
:addClass( 'infobox__itemButtonLink' )
:wikitext( string.format( '[[%s]]', data[ 'link' ] ) )
end

if data[ 'icon' ] then
html:addClass( 'infobox__item--hasIcon' )
html:tag( 'div' )
:addClass( 'infobox__icon' )
:wikitext( string.format( '[[File:%s|16px|link=]]', data[ 'icon' ] ) )
-- Create wrapper for text to align with icon
textWrapper = html:tag( 'div' ):addClass( 'infobox__text' )
end

local dataOrder = { 'label', 'data', 'desc' }

for _, key in ipairs( dataOrder ) do
if data[ key ] then
if type( data[ key ] ) == 'table' then
data[ key ] = table.concat( data[ key ], ', ' )
end

textWrapper:tag( 'div' )
:addClass( 'infobox__' .. key )
:wikitext( data[ key ] )
end
end

-- Add arrow indicator as affordnance
if data[ 'link' ] or data[ 'page' ] then
html:tag( 'div' ):addClass( 'infobox__itemButtonArrow citizen-ui-icon mw-ui-icon-wikimedia-collapse' )
end

return tostring( html )
end

--- Wrap the infobox HTML
---
--- @param innerHtml string inner html of the infobox
--- @param snippetText string text used in snippet in mobile view
--- @return string html infobox html with templatestyles
function methodtable.renderInfobox( self, innerHtml, snippetText )
checkType( 'Module:InfoboxNeue.renderInfobox', 1, self, 'table' )
checkTypeMulti( 'Module:InfoboxNeue.renderInfobox', 2, innerHtml, { 'table', 'string', 'nil' } )
checkType( 'Module:InfoboxNeue.renderInfobox', 3, snippetText, 'string', true )

innerHtml = innerHtml or self.entries
if type( innerHtml ) == 'table' then
innerHtml = table.concat( self.entries )
end

local function renderContent()
local html = mw.html.create( 'div' )
:addClass( 'infobox__content' )
:wikitext( innerHtml )
return tostring( html )
end

local function renderSnippet()
if snippetText == nil then snippetText = mw.title.getCurrentTitle().text end

local html = mw.html.create()
html:tag( 'div' )
:addClass( 'citizen-ui-icon mw-ui-icon-wikimedia-collapse' )
:done()
:tag( 'div' )
:addClass( 'infobox__data' )
:wikitext( string.format( '%s:', t( 'label_quick_facts' ) ) )
:done()
:tag( 'div' )
:addClass( 'infobox__desc' )
:wikitext( snippetText )

return tostring( html )
end

local frame = mw.getCurrentFrame()
local output = getDetailsHTML( {
details = {
class = 'infobox floatright',
content = renderContent()
},
summary = {
class = 'infobox__snippet',
content = renderSnippet()
}
}, frame )

return output .. frame:extensionTag {
name = 'templatestyles', args = { src = 'Module:InfoboxNeue/styles.css' }
} .. table.concat( self.categories )
end

--- Just an accessor for the class method
function methodtable.showDescIfDiff( s1, s2 )
return InfoboxNeue.showDescIfDiff( s1, s2 )
end

--- Format text to show comparison as desc text if two strings are different
---
--- @param s1 string|nil base
--- @param s2 string|nil comparsion
--- @return string|nil html
function InfoboxNeue.showDescIfDiff( s1, s2 )
if s1 == nil or s2 == nil or s1 == s2 then return s1 end
return string.format( '%s <span class="infobox__desc">(%s)</span>', s1, s2 )
end

--- New Instance
---
--- @return table InfoboxNeue
function InfoboxNeue.new( self, config )
local baseConfig = {
-- Flag to discard empty rows
removeEmpty = false,
-- Optional string which is valued as empty
emptyString = nil,
-- Display a placeholder image if addImage does not find an image
displayPlaceholder = true,
-- Placeholder Image
placeholderImage = 'Platzhalter.webp',
}

for k, v in pairs( config or {} ) do
baseConfig[ k ] = v
end

local instance = {
categories = {},
config = baseConfig,
entries = {}
}

setmetatable( instance, metatable )

return instance
end

--- Create an Infobox from args
---
--- @param frame table
--- @return string
function InfoboxNeue.fromArgs( frame )
local instance = InfoboxNeue:new()
local args = require( 'Module:Arguments' ).getArgs( frame )

local sections = {
{ content = {}, col = args[ 'col' ] or 2 }
}

local sectionMap = { default = 1 }

local currentSection

if args[ 'image' ] then
instance:renderImage( args[ 'image' ] )
end

if args[ 'indicator' ] then
instance:renderIndicator( {
data = args[ 'indicator' ],
class = args[ 'indicatorClass' ]
} )
end

if args[ 'title' ] then
instance:renderHeader( {
title = args[ 'title' ],
subtitle = args[ 'subtitle' ],
} )
end

for i = 1, 50, 1 do
if args[ 'section' .. i ] then
currentSection = args[ 'section' .. i ]

table.insert( sections, {
title = currentSection,
subtitle = args[ 'section-subtitle' .. i ],
col = args[ 'section-col' .. i ] or args[ 'col' ] or 2,
content = {}
} )

sectionMap[ currentSection ] = #sections
end

if args[ 'label' .. i ] and args[ 'content' .. i ] then
table.insert( sections[ sectionMap[ (currentSection or 'default') ] ].content,
instance:renderItem( args[ 'label' .. i ], args[ 'content' .. i ] ) )
end
end

for _, section in ipairs( sections ) do
instance:renderSection( {
title = section.title,
subtitle = section.subtitle,
col = section.col,
content = section.content,
} )
end

return instance:renderInfobox( nil, args[ 'snippet' ] )
end


return InfoboxNeue
return InfoboxNeue

Latest revision as of 19:56, 19 November 2024

Message_documentation_title[view][edit][history][purge]
message_transclude_desc
de:Module:InfoboxNeue{{#seo:

|type = website |description = Module:InfoboxNeue is a template page used on the TCG Card Shop Simulator Wiki. Templates are pages that are embedded (transcluded) into other pages to allow for the repetition of information. |site_name = TCG Card Shop Simulator Wiki |locale = en }}

This module is used by Lua modules to build infobox.

Components

Quick facts:
InfoboxNeue
TCG Cards.jpg
Indicator
Title
Subtitle
Item label
Item data
Item label
Item data
Item label
Item data
Section title
Item label
Item data
Item label
Item data
Item label
Item data

Image

infobox:renderImage( 'TCG Cards.jpg' )

Indicator

Parameter Description Type Status
data Data of the indicator string required
desc Description of the indicator string optional
class HTML classes to be added to the indicator string optional
infobox:renderIndicator( {
	data = 'Indicator'
} )

Header

Parameter Description Type Status
title Title of the infobox string required
subtitle Subtitle of the infobox string optional
infobox:renderHeader( {
	title = 'Title',
	subtitle = 'Subtitle'
} )

Message

This is a shortcut way to create a message wrapped in a section.

Parameter Description Type Status
title Title of the message string required
desc Description of the message string optional
infobox:renderMessage( {
	title = 'Message title',
	desc = 'Message description'
} )

Item

Parameter Description Type Status
data Data of the item string required
label Label of the item string optional
desc Description of the item string optional
row Whether to display the item in a row boolean optional
spacebetween Whether to put space between elements in the item boolean optional
colspan Number of columns that the item spans int optional
infobox:renderItem( {
	label = 'Item label',
	data = 'Item data'
} )

Section

This is used to wrap items into a section.

Parameter Description Type Status
content Content of the section string required
title Title of the section string optional
subtitle Subtitle of the section string optional
col Number of columns in the section int optional
class HTML classes to be added to the section string optional
infobox:renderSection( {
	title = 'Section title',
	content = table.concat( sectionTable ),
	col = 3
} )

Layout

Row

Quick facts:
InfoboxNeue
Row layout
This is an example of the row layout.
Bacon
Good
Pancetta
Great
Prosciutto
Wonderful
-- Create items
sectionTable = {
	infobox:renderItem( {
		label = 'Bacon',
		data = 'Good',
		row = true,
		spacebetween = true
	} ),
	infobox:renderItem( {
		label = 'Pancetta',
		data = 'Great',
		row = true,
		spacebetween = true
	} ),
	infobox:renderItem( {
		label = 'Prosciutto',
		data = 'Wonderful',
		row = true,
		spacebetween = true
	} )
}

-- Create section with items
infobox:renderSection( {
	title = 'Row layout',
	subtitle = 'This is an example of the row layout.',
	content = table.concat( sectionTable )
} )

List

Quick facts:
InfoboxNeue
List layout
This is an example of the list layout.
Bacon is good
Bacon ipsum dolor amet burgdoggen boudin spare ribs pork pork chop drumstick beef. Jowl turkey pork, kevin shankle shank shoulder.
Pancetta is great
Kevin pig fatback, alcatra pancetta sirloin venison tri-tip shankle kielbasa meatloaf spare ribs beef. Corned beef salami kielbasa tenderloin swine spare ribs andouille.
Prosciutto is wonderful
Venison chicken meatloaf, ground round swine short ribs shankle short loin tenderloin jerky capicola. Prosciutto venison sirloin beef brisket pancetta.
-- Create items
sectionTable = {
	infobox:renderItem( {
		data = 'Bacon is good',
		desc = 'Bacon ipsum dolor amet burgdoggen boudin spare ribs pork pork chop drumstick beef. Jowl turkey pork, kevin shankle shank shoulder. ',
	} ),
	infobox:renderItem( {
		data = 'Pancetta is great',
		desc = 'Kevin pig fatback, alcatra pancetta sirloin venison tri-tip shankle kielbasa meatloaf spare ribs beef. Corned beef salami kielbasa tenderloin swine spare ribs andouille.',
	} ),
	infobox:renderItem( {
		data = 'Prosciutto is wonderful',
		desc = 'Venison chicken meatloaf, ground round swine short ribs shankle short loin tenderloin jerky capicola. Prosciutto venison sirloin beef brisket pancetta.',
	} )
}

-- Create section with items
infobox:renderSection( {
	title = 'List layout',
	subtitle = 'This is an example of the list layout.',
	content = table.concat( sectionTable )
} )

Grid

Quick facts:
InfoboxNeue
2 col grid layout
This is an example of the two column grid layout.
Bacon
Good
Pancetta
Great
Prosciutto
Wonderful
Capicola
Delightful
3 col grid layout
This is an example of the three column grid layout.
Bacon
Good
Pancetta
Great
Prosciutto
Wonderful
Capicola
Delightful
4 col grid layout
This is an example of the four column grid layout.
Bacon
Good
Pancetta
Great
Prosciutto
Wonderful
Capicola
Delightful
-- Create items
sectionTable = {
	infobox:renderItem( {
		label = 'Bacon',
		data = 'Good'
	} ),
	infobox:renderItem( {
		label = 'Pancetta',
		data = 'Great'
	} ),
	infobox:renderItem( {
		label = 'Prosciutto',
		data = 'Wonderful'
	} ),
	infobox:renderItem( {
		label = 'Capicola',
		data = 'Delightful'
	} )
}

-- Create section with items
infobox:renderSection( {
	title = '2 col grid layout',
	subtitle = 'This is an example of the two column grid layout.',
	content = table.concat( sectionTable ),
	col = 2
} )

infobox:renderSection( {
	title = '3 col grid layout',
	subtitle = 'This is an example of the three column grid layout.',
	content = table.concat( sectionTable ),
	col = 3
} )

infobox:renderSection( {
	title = '4 col grid layout',
	subtitle = 'This is an example of the four column grid layout.',
	content = table.concat( sectionTable ),
	col = 4
} )

local InfoboxNeue = {}

local metatable = {}
local methodtable = {}

local libraryUtil = require( 'libraryUtil' )
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
local i18n = require( 'Module:i18n' ):new()

metatable.__index = methodtable

metatable.__tostring = function ( self )
	return tostring( self:renderInfobox() )
end


--- Wrapper function for Module:i18n.translate
---
--- @param key string The translation key
--- @return string If the key was not found, the key is returned
local function t( key )
	return i18n:translate( key )
end


--- Helper function to restore underscore from space
--- so that it does not screw up the external link wikitext syntax
--- For some reason SMW property converts underscore into space
--- mw.uri.encode can't be used on full URL
local function restoreUnderscore( s )
	return s:gsub( ' ', '%%5F' )
end

--- Helper function to format string to number with separators
--- It is usually use to re-format raw number from SMW into more readable format
local function formatNumber( s )
	local lang = mw.getContentLanguage()
	if s == nil then
		return
	end

	if type( s ) ~= 'number' then
		s = tonumber( s )
	end

	if type( s ) == 'number' then
		return lang:formatNum( s )
	end

	return s
end

-- TODO: Perhaps we should turn this into another module
local function getDetailsHTML( data, frame )
	local summary = frame:extensionTag {
		name = 'summary',
		content = data.summary.content,
		args = {
			class = data.summary.class
		}
	}
	local details = frame:extensionTag {
		name = 'details',
		content = summary .. data.details.content,
		args = {
			class = data.details.class,
			open = true
		}
	}
	return details
end


--- Put table values into a comma-separated list
---
--- @param data table
--- @return string
function methodtable.tableToCommaList( data )
	if type( data ) == 'table' then
		return table.concat( data, ', ' )
	else
		return data
	end
end

--- Show range if value1 and value2 are different
---
--- @param s1 string|nil
--- @param s2 string|nil
--- @return string|nil
function methodtable.formatRange( s1, s2, formatNum )
	if s1 == nil and s2 == nil then
		return
	end

	formatNum = formatNum or false

	if formatNum then
		if s1 then
			s1 = formatNumber( s1 )
		end
		if s2 then
			s2 = formatNumber( s2 )
		end
	end

	if s1 and s2 and s1 ~= s2 then
		return s1 .. ' – ' .. s2
	end

	return s1 or s2
end

--- Append unit to the value if exists
---
--- @param s string
--- @param unit string
--- @return string|nil
function methodtable.addUnitIfExists( s, unit )
	if s == nil then
		return
	end

	return s .. ' ' .. unit
end

--- Shortcut to return the HTML of the infobox message component as string
---
--- @param data table {title, desc)
--- @return string html
function methodtable.renderMessage( self, data, noInsert )
	checkType( 'Module:InfoboxNeue.renderMessage', 1, self, 'table' )
	checkType( 'Module:InfoboxNeue.renderMessage', 2, data, 'table' )
	checkType( 'Module:InfoboxNeue.renderMessage', 3, noInsert, 'boolean', true )

	noInsert = noInsert or false

	local item = self:renderSection( { content = self:renderItem( { data = data.title, desc = data.desc } ) }, noInsert )

	if not noInsert then
		table.insert( self.entries, item )
	end

	return item
end

--- Return the HTML of the infobox image component as string
---
--- @param filename string
--- @return string html
function methodtable.renderImage( self, filename )
	checkType( 'Module:InfoboxNeue.renderImage', 1, self, 'table' )

	local hasPlaceholderImage = false

	if type( filename ) ~= 'string' and self.config.displayPlaceholder == true then
		hasPlaceholderImage = true
		filename = self.config.placeholderImage
		-- Add tracking category for infoboxes using placeholder image
		table.insert( self.categories,
			string.format( '[[Category:%s]]', t( 'category_infobox_using_placeholder_image' ) )
		)
	end

	if type( filename ) ~= 'string' then
		return ''
	end

	local parts = mw.text.split( filename, ':', true )
	if #parts > 1 then
		table.remove( parts, 1 )
		filename = table.concat( parts, ':' )
	end

	local html = mw.html.create( 'div' )
		:addClass( 'infobox__image' )
		:wikitext( string.format( '[[File:%s|400px]]', filename ) )

	if hasPlaceholderImage == true then
		local icon = mw.html.create( 'span' ):addClass( 'citizen-ui-icon mw-ui-icon-wikimedia-upload' )
		-- TODO: Point the Upload link to a specific file name
		html:tag( 'div' ):addClass( 'infobox__image-upload' )
			:wikitext( string.format( '[[%s|%s]]', 'Special:UploadWizard',
				tostring( icon ) .. t( 'label_upload_image' ) ) )
	end

	local item = tostring( html )

	table.insert( self.entries, item )

	return item
end

--- Return the HTML of the infobox indicator component as string
---
--- @param data table {data, class, color, nopadding)
--- @return string html
function methodtable.renderIndicator( self, data )
	checkType( 'Module:InfoboxNeue.renderIndicator', 1, self, 'table' )
	checkType( 'Module:InfoboxNeue.renderIndicator', 2, data, 'table' )

	if data == nil or data[ 'data' ] == nil or data[ 'data' ] == '' then return '' end

	local html = mw.html.create( 'div' ):addClass( 'infobox__indicators' )

	local htmlClasses = {
		'infobox__indicator'
	}

	if data[ 'class' ] then
		table.insert( htmlClasses, data[ 'class' ] )
	end

	if data[ 'color' ] then
		table.insert( htmlClasses, 'infobox__indicator--' .. data[ 'color' ] )
	end

	if data[ 'nopadding' ] == true then
		table.insert( htmlClasses, 'infobox__indicator--nopadding' )
	end

	html:wikitext(
		self:renderItem(
			{
				[ 'data' ] = data[ 'data' ],
				[ 'class' ] = table.concat( htmlClasses, ' ' ),
				row = true,
				spacebetween = true
			}
		)
	)

	local item = tostring( html )

	table.insert( self.entries, item )

	return item
end

--- Return the HTML of the infobox header component as string
---
--- @param data table {title, subtitle, badge)
--- @return string html
function methodtable.renderHeader( self, data )
	checkType( 'Module:InfoboxNeue.renderHeader', 1, self, 'table' )
	checkTypeMulti( 'Module:InfoboxNeue.renderHeader', 2, data, { 'table', 'string' } )

	if type( data ) == 'string' then
		data = {
			title = data
		}
	end

	if data == nil or data[ 'title' ] == nil then return '' end

	local html = mw.html.create( 'div' ):addClass( 'infobox__header' )

	if data[ 'badge' ] then
		html:tag( 'div' )
			:addClass( 'infobox__item infobox__badge' )
			:wikitext( data[ 'badge' ] )
	end

	local titleItem = mw.html.create( 'div' ):addClass( 'infobox__item' )

	titleItem:tag( 'div' )
		:addClass( 'infobox__title' )
		:wikitext( data[ 'title' ] )

	if data[ 'subtitle' ] then
		titleItem:tag( 'div' )
		-- Subtitle is always data
			:addClass( 'infobox__subtitle infobox__data' )
			:wikitext( data[ 'subtitle' ] )
	end

	html:node( titleItem )

	local item = tostring( html )

	table.insert( self.entries, item )

	return item
end

--- Wrap the HTML into an infobox section
---
--- @param data table {title, subtitle, content, border, col, class}
--- @param noInsert boolean whether to insert this section into the internal table table
--- @return string html
function methodtable.renderSection( self, data, noInsert )
	checkType( 'Module:InfoboxNeue.renderSection', 1, self, 'table' )
	checkType( 'Module:InfoboxNeue.renderSection', 2, data, 'table' )
	checkType( 'Module:InfoboxNeue.renderSection', 3, noInsert, 'boolean', true )

	noInsert = noInsert or false

	if type( data.content ) == 'table' then
		data.content = table.concat( data.content )
	end

	if data == nil or data[ 'content' ] == nil or data[ 'content' ] == '' then return '' end

	local html = mw.html.create( 'div' ):addClass( 'infobox__section' )

	if data[ 'title' ] then
		local header = html:tag( 'div' ):addClass( 'infobox__sectionHeader' )
		header:tag( 'div' )
			:addClass( 'infobox__sectionTitle' )
			:wikitext( data[ 'title' ] )
		if data[ 'subtitle' ] then
			header:tag( 'div' )
				:addClass( 'infobox__sectionSubtitle' )
				:wikitext( data[ 'subtitle' ] )
		end
	end

	local content = html:tag( 'div' )
	content:addClass( 'infobox__sectionContent' )
		:wikitext( data[ 'content' ] )

	if data[ 'border' ] == false then html:addClass( 'infobox__section--noborder' ) end
	if data[ 'col' ] then content:addClass( 'infobox__grid--cols-' .. data[ 'col' ] ) end
	if data[ 'class' ] then html:addClass( data[ 'class' ] ) end

	local item = tostring( html )

	if not noInsert then
		table.insert( self.entries, item )
	end

	return item
end

--- Return the HTML of the infobox link button component as string
---
--- @param data table {label, link, page}
--- @return string html
function methodtable.renderLinkButton( self, data )
	checkType( 'Module:InfoboxNeue.renderLinkButton', 1, self, 'table' )
	checkType( 'Module:InfoboxNeue.renderLinkButton', 2, data, 'table' )

	if data == nil or data[ 'label' ] == nil or (data[ 'link' ] == nil and data[ 'page' ] == nil) then return '' end

	--- Render multiple linkButton when link is a table
	if type( data[ 'link' ] ) == 'table' then
		local htmls = {}

		for i, url in ipairs( data[ 'link' ] ) do
			table.insert( htmls,
				self:renderLinkButton( {
					label = string.format( '%s %d', data[ 'label' ], i ),
					link = url
				} )
			)
		end

		return table.concat( htmls )
	end

	local html = mw.html.create( 'div' ):addClass( 'infobox__linkButton' )

	if data[ 'link' ] then
		html:wikitext( string.format( '[%s %s]', restoreUnderscore( data[ 'link' ] ), data[ 'label' ] ) )
	elseif data[ 'page' ] then
		html:wikitext( string.format( '[[%s|%s]]', data[ 'page' ], data[ 'label' ] ) )
	end

	return tostring( html )
end

--- Return the HTML of the infobox footer component as string
---
--- @param data table {content, button}
--- @return string html
function methodtable.renderFooter( self, data )
	checkType( 'Module:InfoboxNeue.renderFooter', 1, self, 'table' )
	checkType( 'Module:InfoboxNeue.renderFooter', 2, data, 'table' )

	if data == nil then return '' end

	-- Checks if an input is of type 'table' or 'string' and if it is not empty
	local function isNonEmpty( input )
		return (type( input ) == 'table' and next( input ) ~= nil) or (type( input ) == 'string' and #input > 0)
	end

	local hasContent = isNonEmpty( data[ 'content' ] )
	local hasButton = isNonEmpty( data[ 'button' ] ) and isNonEmpty( data[ 'button' ][ 'content' ] ) and
		isNonEmpty( data[ 'button' ][ 'label' ] )

	if not hasContent and not hasButton then return '' end

	local html = mw.html.create( 'div' ):addClass( 'infobox__footer' )

	if hasContent then
		local content = data[ 'content' ]
		if type( content ) == 'table' then content = table.concat( content ) end

		html:addClass( 'infobox__footer--has-content' )
		html:tag( 'div' )
			:addClass( 'infobox__section' )
			:wikitext( content )
	end

	if hasButton then
		html:addClass( 'infobox__footer--has-button' )
		local buttonData = data[ 'button' ]
		local button = html:tag( 'div' ):addClass( 'infobox__button' )
		local label = button:tag( 'div' ):addClass( 'infobox__buttonLabel' )

		if buttonData[ 'icon' ] ~= nil then
			label:wikitext( string.format( '[[File:%s|16px|link=]]%s', buttonData[ 'icon' ], buttonData[ 'label' ] ) )
		else
			label:wikitext( buttonData[ 'label' ] )
		end

		if buttonData[ 'type' ] == 'link' then
			button:tag( 'div' )
				:addClass( 'infobox__buttonLink' )
				:wikitext( buttonData[ 'content' ] )
		elseif buttonData[ 'type' ] == 'popup' then
			button:tag( 'div' )
				:addClass( 'infobox__buttonCard' )
				:wikitext( buttonData[ 'content' ] )
		end
	end

	local item = tostring( html )

	table.insert( self.entries, item )

	return item
end

--- Return the HTML of the infobox footer button component as string
---
--- @param data table {icon, label, type, content}
--- @return string html
function methodtable.renderFooterButton( self, data )
	checkType( 'Module:InfoboxNeue.renderFooterButton', 1, self, 'table' )
	checkType( 'Module:InfoboxNeue.renderFooterButton', 2, data, 'table' )

	if data == nil then return '' end

	return self:renderFooter( { button = data } )
end

--- Return the HTML of the infobox item component as string
---
--- @param data table {label, data, desc, class, tooltip, icon, row, spacebetween, colspan)
--- @param content string|number|nil optional
--- @return string html
function methodtable.renderItem( self, data, content )
	checkType( 'Module:InfoboxNeue.renderItem', 1, self, 'table' )
	checkTypeMulti( 'Module:InfoboxNeue.renderItem', 2, data, { 'table', 'string' } )
	checkTypeMulti( 'Module:InfoboxNeue.renderItem', 3, content, { 'string', 'number', 'nil' } )

	-- The arguments are not passed as a table
	-- Allows to call this as box:renderItem( 'Label', 'Data' )
	if content ~= nil then
		data = {
			label = data,
			data = content
		}
	end

	if data == nil or data[ 'data' ] == nil or data[ 'data' ] == '' then return '' end

	if self.config.removeEmpty == true and data[ 'data' ] == self.config.emptyString then
		return ''
	end

	local html = mw.html.create( 'div' ):addClass( 'infobox__item' )

	if data[ 'class' ] then html:addClass( data[ 'class' ] ) end
	if data[ 'tooltip' ] then html:attr( 'title', data[ 'tooltip' ] ) end
	if data[ 'row' ] == true then html:addClass( 'infobox__grid--row' ) end
	if data[ 'spacebetween' ] == true then html:addClass( 'infobox__grid--space-between' ) end
	if data[ 'colspan' ] then html:addClass( 'infobox__grid--col-span-' .. data[ 'colspan' ] ) end

	local textWrapper = html

	if data[ 'link' ] then
		html:addClass( 'infobox__itemButton' )
		html:tag( 'div' )
			:addClass( 'infobox__itemButtonLink' )
			:wikitext( string.format( '[%s]', data[ 'link' ] ) )
	elseif data[ 'page' ] then
		html:addClass( 'infobox__itemButton' )
		html:tag( 'div' )
			:addClass( 'infobox__itemButtonLink' )
			:wikitext( string.format( '[[%s]]', data[ 'link' ] ) )
	end

	if data[ 'icon' ] then
		html:addClass( 'infobox__item--hasIcon' )
		html:tag( 'div' )
			:addClass( 'infobox__icon' )
			:wikitext( string.format( '[[File:%s|16px|link=]]', data[ 'icon' ] ) )
		-- Create wrapper for text to align with icon
		textWrapper = html:tag( 'div' ):addClass( 'infobox__text' )
	end

	local dataOrder = { 'label', 'data', 'desc' }

	for _, key in ipairs( dataOrder ) do
		if data[ key ] then
			if type( data[ key ] ) == 'table' then
				data[ key ] = table.concat( data[ key ], ', ' )
			end

			textWrapper:tag( 'div' )
				:addClass( 'infobox__' .. key )
				:wikitext( data[ key ] )
		end
	end

	-- Add arrow indicator as affordnance
	if data[ 'link' ] or data[ 'page' ] then
		html:tag( 'div' ):addClass( 'infobox__itemButtonArrow citizen-ui-icon mw-ui-icon-wikimedia-collapse' )
	end

	return tostring( html )
end

--- Wrap the infobox HTML
---
--- @param innerHtml string inner html of the infobox
--- @param snippetText string text used in snippet in mobile view
--- @return string html infobox html with templatestyles
function methodtable.renderInfobox( self, innerHtml, snippetText )
	checkType( 'Module:InfoboxNeue.renderInfobox', 1, self, 'table' )
	checkTypeMulti( 'Module:InfoboxNeue.renderInfobox', 2, innerHtml, { 'table', 'string', 'nil' } )
	checkType( 'Module:InfoboxNeue.renderInfobox', 3, snippetText, 'string', true )

	innerHtml = innerHtml or self.entries
	if type( innerHtml ) == 'table' then
		innerHtml = table.concat( self.entries )
	end

	local function renderContent()
		local html = mw.html.create( 'div' )
			:addClass( 'infobox__content' )
			:wikitext( innerHtml )
		return tostring( html )
	end

	local function renderSnippet()
		if snippetText == nil then snippetText = mw.title.getCurrentTitle().text end

		local html = mw.html.create()
		
		html:tag( 'div' )
			:addClass( 'citizen-ui-icon mw-ui-icon-wikimedia-collapse' )
			:done()
			:tag( 'div' )
			:addClass( 'infobox__data' )
			:wikitext( string.format( '%s:', t( 'label_quick_facts' ) ) )
			:done()
			:tag( 'div' )
			:addClass( 'infobox__desc' )
			:wikitext( snippetText )

		return tostring( html )
	end

	local frame =  mw.getCurrentFrame()
	local output = getDetailsHTML( {
		details = {
			class = 'infobox floatright',
			content = renderContent()
		},
		summary = {
			class = 'infobox__snippet',
			content = renderSnippet()
		}
	}, frame )

	return output .. frame:extensionTag {
		name = 'templatestyles', args = { src = 'Module:InfoboxNeue/styles.css' }
	} .. table.concat( self.categories )
end

--- Just an accessor for the class method
function methodtable.showDescIfDiff( s1, s2 )
	return InfoboxNeue.showDescIfDiff( s1, s2 )
end

--- Format text to show comparison as desc text if two strings are different
---
--- @param s1 string|nil base
--- @param s2 string|nil comparsion
--- @return string|nil html
function InfoboxNeue.showDescIfDiff( s1, s2 )
	if s1 == nil or s2 == nil or s1 == s2 then return s1 end
	return string.format( '%s <span class="infobox__desc">(%s)</span>', s1, s2 )
end

--- New Instance
---
--- @return table InfoboxNeue
function InfoboxNeue.new( self, config )
	local baseConfig = {
		-- Flag to discard empty rows
		removeEmpty = false,
		-- Optional string which is valued as empty
		emptyString = nil,
		-- Display a placeholder image if addImage does not find an image
		displayPlaceholder = true,
		-- Placeholder Image
		placeholderImage = 'Platzhalter.webp',
	}

	for k, v in pairs( config or {} ) do
		baseConfig[ k ] = v
	end

	local instance = {
		categories = {},
		config = baseConfig,
		entries = {}
	}

	setmetatable( instance, metatable )

	return instance
end

--- Create an Infobox from args
---
--- @param frame table
--- @return string
function InfoboxNeue.fromArgs( frame )
	local instance = InfoboxNeue:new()
	local args = require( 'Module:Arguments' ).getArgs( frame )

	local sections = {
		{ content = {}, col = args[ 'col' ] or 2 }
	}

	local sectionMap = { default = 1 }

	local currentSection

	if args[ 'image' ] then
		instance:renderImage( args[ 'image' ] )
	end

	if args[ 'indicator' ] then
		instance:renderIndicator( {
			data = args[ 'indicator' ],
			class = args[ 'indicatorClass' ]
		} )
	end

	if args[ 'title' ] then
		instance:renderHeader( {
			title = args[ 'title' ],
			subtitle = args[ 'subtitle' ],
		} )
	end

	for i = 1, 50, 1 do
		if args[ 'section' .. i ] then
			currentSection = args[ 'section' .. i ]

			table.insert( sections, {
				title = currentSection,
				subtitle = args[ 'section-subtitle' .. i ],
				col = args[ 'section-col' .. i ] or args[ 'col' ] or 2,
				content = {}
			} )

			sectionMap[ currentSection ] = #sections
		end

		if args[ 'label' .. i ] and args[ 'content' .. i ] then
			table.insert( sections[ sectionMap[ (currentSection or 'default') ] ].content,
				instance:renderItem( args[ 'label' .. i ], args[ 'content' .. i ] ) )
		end
	end

	for _, section in ipairs( sections ) do
		instance:renderSection( {
			title = section.title,
			subtitle = section.subtitle,
			col = section.col,
			content = section.content,
		} )
	end

	return instance:renderInfobox( nil, args[ 'snippet' ] )
end

return InfoboxNeue