Module:Infobox
Your guide to Albion
More actions
Documentation for this module may be created at Module:Infobox/doc
-- =============================================================================
-- Module:Infobox v5 — fully data-driven, deterministic order.
--
-- Editors NEVER edit this file to add a new field. Any parameter you pass
-- to {{Infobox|...}} that isn't one of the small set of reserved keys will
-- render as a labelled row. The parameter name becomes the row label.
--
-- ROW ORDER:
-- * By default, rows render in alphabetical order by parameter name.
-- * To control order, set `order = Race, Alignment, Status, …`. Listed
-- fields render first in that order; anything else renders alphabetically
-- after them.
--
-- RESERVED KEYS (case-insensitive — these go to specific UI slots, not rows):
-- type — category chip above the title
-- name — the big title (defaults to page name)
-- subtitle — italic line under the title
-- image — image filename (no File: prefix)
-- caption — caption under the image
-- order — comma-separated list of field names to render first
--
-- Everything else is a labelled row.
-- =============================================================================
local p = {}
local RESERVED = {
['type'] = true,
['name'] = true,
['subtitle'] = true,
['image'] = true,
['caption'] = true,
['order'] = true,
}
local function trim( s )
if s == nil then return nil end
s = mw.text.trim( tostring( s ) )
if s == "" then return nil end
return s
end
-- Turn a parameter name into a display label:
-- race -> Race
-- first_appeared -> First appeared (underscores become spaces)
-- First appeared -> First appeared (already nicely cased)
-- voiced-by -> Voiced by (dashes become spaces)
-- Only the first letter is capitalised; later words keep their case.
local function formatLabel( key )
local s = tostring( key )
s = ( s:gsub( '[_%-]', ' ' ) )
if #s > 0 then
s = s:sub( 1, 1 ):upper() .. s:sub( 2 )
end
return s
end
-- Split a comma-separated "order" string into an array of trimmed names.
local function parseOrder( raw )
local list = {}
if raw then
for piece in string.gmatch( raw, '([^,]+)' ) do
local t = trim( piece )
if t then table.insert( list, t ) end
end
end
return list
end
function p.main( frame )
local parent = frame:getParent() or frame
local args = parent.args or {}
-- ---- Reserved identity fields
local typeRaw = trim( args.type )
local name = trim( args.name ) or mw.title.getCurrentTitle().text
local subtitle = trim( args.subtitle )
local image = trim( args.image )
local caption = trim( args.caption )
local orderList = parseOrder( trim( args.order ) )
-- ---- Root element + identity slots
local root = mw.html.create( 'div' ):addClass( 'fable-infobox' )
if typeRaw then
root:attr( 'data-infobox-type', string.lower( typeRaw ) )
root:tag( 'div' ):addClass( 'fable-infobox__chip' )
:wikitext( formatLabel( typeRaw ) )
end
root:tag( 'div' ):addClass( 'fable-infobox__title' ):wikitext( name )
if subtitle then
root:tag( 'div' ):addClass( 'fable-infobox__subtitle' ):wikitext( subtitle )
end
if image then
local w = root:tag( 'div' ):addClass( 'fable-infobox__image' )
w:wikitext( string.format( '[[File:%s|frameless|300x300px|center]]', image ) )
if caption then
w:tag( 'div' ):addClass( 'fable-infobox__caption' ):wikitext( caption )
end
end
-- ---- Collect every non-reserved, non-empty arg into a key list.
-- parent:argumentPairs() returns args in unspecified order, so we
-- gather first, then sort, so output is deterministic.
local nonReserved = {}
if parent.argumentPairs then
for key, value in parent:argumentPairs() do
if type( key ) == 'string' and not RESERVED[ key:lower() ] then
local v = trim( value )
if v then
nonReserved[ key ] = v
end
end
end
end
-- ---- Build final render order: explicit order list first, then the
-- remainder alphabetically.
local rendered = {}
local seen = {}
for _, k in ipairs( orderList ) do
if nonReserved[ k ] and not seen[ k ] then
table.insert( rendered, k )
seen[ k ] = true
end
end
local rest = {}
for k in pairs( nonReserved ) do
if not seen[ k ] then table.insert( rest, k ) end
end
table.sort( rest, function( a, b ) return a:lower() < b:lower() end )
for _, k in ipairs( rest ) do table.insert( rendered, k ) end
-- ---- Render rows in the final order.
local body = root:tag( 'div' ):addClass( 'fable-infobox__rows' )
for _, key in ipairs( rendered ) do
local v = nonReserved[ key ]
local r = body:tag( 'div' ):addClass( 'fable-infobox__row' )
r:tag( 'div' ):addClass( 'fable-infobox__label' ):wikitext( formatLabel( key ) )
r:tag( 'div' ):addClass( 'fable-infobox__value' ):wikitext( v )
end
local styles = frame:extensionTag{
name = 'templatestyles',
args = { src = 'Template:Infobox/styles.css' },
}
return styles .. tostring( root )
end
return p