Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
You must create an account or log in to edit.

Module:InfoboxNeue: Difference between revisions

From Amaranth Legacy, available at amaranth-legacy.community
Content deleted Content added
No edit summary
Tag: Reverted
m Removed protection from "Module:InfoboxNeue"
 
(196 intermediate revisions by the same user not shown)
Line 1: Line 1:
-- original source: https://github.com/The-Star-Citizen-Wikis/SharedModules/blob/e6e1ef118b211e8247e0e1bc9c0257879579c147/InfoboxNeue/InfoboxNeue.lua
-- heavily modified for Amaranth Legacy

local InfoboxNeue = {}
local InfoboxNeue = {}


Line 4: Line 7:
local methodtable = {}
local methodtable = {}


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


metatable.__index = methodtable
metatable.__index = methodtable


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


local function orderedPairs(t, f)

local a = {}
--- Wrapper function for Module:i18n.translate
for n in pairs(t) do table.insert(a, n) end
---
table.sort(a, f)
--- @param key string The translation key
local i = 0 -- iterator variable
--- @return string If the key was not found, the key is returned
local iter = function() -- iterator function
local function t( key )
i = i + 1
return i18n:translate( key )
if a[i] == nil then
return nil
else
return a[i], t[a[i]]
end
end
return iter
end
end


local function removeprefix(s, p)

return (s:sub(0, #p) == p) and s:sub(#p + 1) or s
--- 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
end


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


--- render an image into HTML

--- 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
--- @param filename string
--- @return string html
--- @param caption string
function methodtable.renderImage( self, filename )
function methodtable.renderImage(self, filename, caption)
-- input validation
checkType( 'Module:InfoboxNeue.renderImage', 1, self, 'table' )
checkType('Module:InfoboxNeue.renderImage', 1, self, 'table')
checkType('Module:InfoboxNeue.renderImage', 2, filename, 'string')
checkType('Module:InfoboxNeue.renderImage', 3, caption, 'string', true)


-- input is wikitext
local hasPlaceholderImage = false
filename = filename:gsub('^%[%[File:(.+)]]', '%1')


-- input is complete file page name
if type( filename ) ~= 'string' and self.config.displayPlaceholder == true then
filename = filename:gsub('^File:(.+)', '%1')
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


local html
if type( filename ) ~= 'string' then
if filename:match('--gallery-') then
return ''
-- <gallery>
html = mw.html.create('div')
:addClass('infobox__gallery')
:wikitext(filename)
else
-- file name
html = mw.html.create('div')
:addClass('infobox__image')
:wikitext(string.format('[[File:%s|400px|class=attribute-loading-unset]]', filename))
end
end


if caption ~= nil then
local parts = mw.text.split( filename, ':', true )
html:tag('div')
if #parts > 1 then
:addClass('infobox__caption')
table.remove( parts, 1 )
:wikitext(caption)
filename = table.concat( parts, ':' )
end
end


table.insert(self.entries, tostring(html))
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
end


--- Return the HTML of the infobox indicator component as string
--- wrap the infobox section into ready-to-use HTML
---
---
--- @param data table {data, class, color, nopadding)
--- @param data table
function methodtable.renderSection(self, data)
--- @return string html
-- input validation
function methodtable.renderIndicator( self, data )
checkType( 'Module:InfoboxNeue.renderIndicator', 1, self, 'table' )
checkType('Module:InfoboxNeue.renderSection', 1, self, 'table')
checkType( 'Module:InfoboxNeue.renderIndicator', 2, data, 'table' )
checkType('Module:InfoboxNeue.renderSection', 2, data, 'table')


-- if there's nothing to render, return an empty string
if data == nil or data[ 'data' ] == nil or data[ 'data' ] == '' then return '' end
if data['content'] == nil or next(data['content']) == nil then return end


-- section wrapper
local html = mw.html.create( 'div' ):addClass( 'infobox__indicators' )
local html = mw.html.create('div'):addClass('infobox__section')


-- already checked that there's a title at this point
local htmlClasses = {
'infobox__indicator'
}


-- section header
if data[ 'class' ] then
local header = html:tag('div')
table.insert( htmlClasses, data[ 'class' ] )
:addClass('infobox__sectionTitle')
end
:wikitext(data['title'])


-- content wrapper
if data[ 'color' ] then
local content = html:tag('div')
table.insert( htmlClasses, 'infobox__indicator--' .. data[ 'color' ] )
:addClass('infobox__sectionContent')
end
:wikitext(table.concat(data['content']))


-- set column count
if data[ 'nopadding' ] == true then
if data['col'] then content:addClass('infobox__grid--cols-' .. data['col']) end
table.insert( htmlClasses, 'infobox__indicator--nopadding' )
-- set grid direction
end
if data['row'] then content:addClass('infobox__grid--row') 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 )


table.insert(self.entries, tostring(html))
return item
end
end


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


-- if there's nothing to render, return an empty string
if type( data ) == 'string' then
if data['content'] == nil or data['content'] == '' 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 ''
return ''
end
end


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


if data[ 'class' ] then html:addClass( data[ 'class' ] ) end
if data['spacebetween'] == true then html:addClass('infobox__grid--space-between') end
if data[ 'tooltip' ] then html:attr( 'title', data[ 'tooltip' ] ) end
if data['colspan'] then html:addClass('infobox__grid--col-span-' .. data['colspan']) 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


-- already checked that there's a label and content at this point
local textWrapper = html


html:tag('div')
if data[ 'link' ] then
html:addClass( 'infobox__itemButton' )
:addClass('infobox__label')
html:tag( 'div' )
:wikitext(data['label'])
: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


html:tag('poem')
if data[ 'icon' ] then
html:addClass( 'infobox__item--hasIcon' )
:addClass('infobox__content')
html:tag( 'div' )
:wikitext(data['content'])
: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


return tostring(html)
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 )
:newline()
local wikitext = mw.text.split( data[ key ], "\n" );
table.foreach( wikitext, function(key, value) wikitext[key] = '<p>' .. value .. '</p>' end )
textWrapper:wikitext( unpack( wikitext ) )
:done()
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
end


--- Wrap the infobox HTML
--- wrap the infobox into ready-to-use HTML
---
---
--- @param innerHtml string inner html of the infobox
--- @param title string text used as the header
--- @return string
--- @param snippetText string text used in snippet in mobile view
function methodtable.renderInfobox(self, title, open)
--- @return string html infobox html with templatestyles
-- input validation
function methodtable.renderInfobox( self, innerHtml, snippetText )
checkType( 'Module:InfoboxNeue.renderInfobox', 1, self, 'table' )
checkType('Module:InfoboxNeue.renderInfobox', 1, self, 'table')
checkTypeMulti( 'Module:InfoboxNeue.renderInfobox', 2, innerHtml, { 'table', 'string', 'nil' } )
checkType('Module:InfoboxNeue.renderInfobox', 2, title, 'string', true)
checkType( 'Module:InfoboxNeue.renderInfobox', 3, snippetText, 'string', true )
checkType('Module:InfoboxNeue.renderInfobox', 3, open, 'boolean')

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


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


local function renderSnippet()
local function renderHeader()
local html = mw.html.create('div')
if snippetText == nil then snippetText = mw.title.getCurrentTitle().text end
:addClass('infobox__title')

:wikitext(title)
local html = mw.html.create()
return tostring(html)
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
end


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


return frame:extensionTag {
return frame:extensionTag { name = 'templatestyles', args = { src = 'Module:InfoboxNeue/styles.css' } }
.. output
name = 'templatestyles', args = { src = 'Module:InfoboxNeue/styles.css' }
} .. output .. 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
end


Line 607: Line 205:
---
---
--- @return table InfoboxNeue
--- @return table InfoboxNeue
function InfoboxNeue.new( self, config )
function InfoboxNeue.new()
local baseConfig = {
-- Flag to discard empty rows
removeEmpty = true,
-- 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 = 'Unknown.png',
}

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

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


setmetatable( instance, metatable )
setmetatable(instance, metatable)


return instance
return instance
end
end


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

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

local sectionMap = { default = 1 }


-- section tracking
local sections = {}
local sectionMap = {}
local currentSection
local currentSection


if args[ 'image' ] then
-- image rendering
instance:renderImage( args[ 'image' ] )
if args['image'] then
-- backwards compatibility: render a single image
if args[ 'indicator' ] then
instance:renderIndicator( {
instance:renderImage(args['image'], args['caption'])
data = args[ 'indicator' ],
class = args[ 'indicatorClass' ]
} )
end
else
else
for i = 1, 5, 1 do
for i = 1, 10, 1 do
if args[ 'image' .. i ] then
if args['image' .. i] then
-- render image1 through image3
instance:renderImage( args[ 'image' .. i ] )
if args[ 'indicator' .. i ] then
instance:renderImage(args['image' .. i], args['caption' .. i])
instance:renderIndicator( {
data = args[ 'indicator' .. i ],
class = args[ 'indicatorClass' .. i ]
} )
end
end
end
end
end
end
end


-- for each argument (ordered alphabetically)
if args[ 'title' ] then
for key, value in orderedPairs(args) do
instance:renderHeader( {
-- if it's a label
title = args[ 'title' ],
if type(string.match(key, '^label')) ~= 'nil' then
subtitle = args[ 'subtitle' ],
-- if there's a section with this label's number
} )
if args['section' .. removeprefix(key, 'label')] then
end
-- set the current section
currentSection = args['section' .. removeprefix(key, 'label')]


-- insert a new section
for i = 1, 500, 1 do
table.insert(sections, {
if args[ 'section' .. i ] then
title = currentSection,
currentSection = args[ 'section' .. i ]
col = args['section-col' .. removeprefix(key, 'label')] or 2,
row = args['section-row' .. removeprefix(key, 'label')] or false,
content = {}
})


-- map the section name to the section number
table.insert( sections, {
sectionMap[currentSection] = #sections
title = currentSection,
end
subtitle = args[ 'section-subtitle' .. i ],
col = args[ 'section-col' .. i ] or args[ 'col' ] or 2,
content = {}
} )


-- set up the data for the item
sectionMap[ currentSection ] = #sections
end

if args[ 'label' .. i ] and args[ 'content' .. i ] then
local data = {
local data = {
label = args['label' .. i],
label = value,
data = args[ 'content' .. i ]
content = args['content' .. removeprefix(key, 'label')],
colspan = args['colspan' .. removeprefix(key, 'label')]
}
}

local renderedItem = instance:renderItem(data)
if args['colspan' .. i] then
if renderedItem ~= '' then
data.colspan = args['colspan' .. i]
-- add to the section's content
table.insert(sections[sectionMap[currentSection]].content, renderedItem)
end
end
table.insert( sections[ sectionMap[ (currentSection or 'default') ] ].content,
instance:renderItem( data, nil ) )
end
end
end
end


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


-- render the entire infobox and return
return instance:renderInfobox( nil, args[ 'snippet' ] )
return instance:renderInfobox(
args['title'] or mw.ext.displaytitle.get(mw.title.getCurrentTitle().fullText),
yesno(args['open'] or true)
)
end
end



Latest revision as of 10:44, September 11, 2025

This module is used for Template:InfoboxNeue. See the template's page for more information.


-- original source: https://github.com/The-Star-Citizen-Wikis/SharedModules/blob/e6e1ef118b211e8247e0e1bc9c0257879579c147/InfoboxNeue/InfoboxNeue.lua
-- heavily modified for Amaranth Legacy

local InfoboxNeue = {}

local metatable = {}
local methodtable = {}

local yesno = require('Module:Yesno')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType

metatable.__index = methodtable

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

local function orderedPairs(t, f)
	local a = {}
	for n in pairs(t) do table.insert(a, n) end
	table.sort(a, f)
	local i = 0          -- iterator variable
	local iter = function() -- iterator function
		i = i + 1
		if a[i] == nil then
			return nil
		else
			return a[i], t[a[i]]
		end
	end
	return iter
end

local function removeprefix(s, p)
	return (s:sub(0, #p) == p) and s:sub(#p + 1) or s
end

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 = data.details.open
		}
	}
	return details
end

--- render an image into HTML
---
--- @param filename string
--- @param caption string
function methodtable.renderImage(self, filename, caption)
	-- input validation
	checkType('Module:InfoboxNeue.renderImage', 1, self, 'table')
	checkType('Module:InfoboxNeue.renderImage', 2, filename, 'string')
	checkType('Module:InfoboxNeue.renderImage', 3, caption, 'string', true)

	-- input is wikitext
	filename = filename:gsub('^%[%[File:(.+)]]', '%1')

	-- input is complete file page name
	filename = filename:gsub('^File:(.+)', '%1')

	local html
	if filename:match('--gallery-') then
		-- <gallery>
		html = mw.html.create('div')
			:addClass('infobox__gallery')
			:wikitext(filename)
	else
		-- file name
		html = mw.html.create('div')
			:addClass('infobox__image')
			:wikitext(string.format('[[File:%s|400px|class=attribute-loading-unset]]', filename))
	end

	if caption ~= nil then
		html:tag('div')
			:addClass('infobox__caption')
			:wikitext(caption)
	end

	table.insert(self.entries, tostring(html))
end

--- wrap the infobox section into ready-to-use HTML
---
--- @param data table
function methodtable.renderSection(self, data)
	-- input validation
	checkType('Module:InfoboxNeue.renderSection', 1, self, 'table')
	checkType('Module:InfoboxNeue.renderSection', 2, data, 'table')

	-- if there's nothing to render, return an empty string
	if data['content'] == nil or next(data['content']) == nil then return end

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

	-- already checked that there's a title at this point

	-- section header
	local header = html:tag('div')
		:addClass('infobox__sectionTitle')
		:wikitext(data['title'])

	-- content wrapper
	local content = html:tag('div')
		:addClass('infobox__sectionContent')
		:wikitext(table.concat(data['content']))

	-- set column count
	if data['col'] then content:addClass('infobox__grid--cols-' .. data['col']) end
	-- set grid direction
	if data['row'] then content:addClass('infobox__grid--row') end

	table.insert(self.entries, tostring(html))
end

--- render an item in the infobox
---
--- @param data table
--- @return string
function methodtable.renderItem(self, data)
	-- input validation
	checkType('Module:InfoboxNeue.renderItem', 1, self, 'table')
	checkType('Module:InfoboxNeue.renderItem', 2, data, 'table')

	-- if there's nothing to render, return an empty string
	if data['content'] == nil or data['content'] == '' then
		return ''
	end

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

	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

	-- already checked that there's a label and content at this point

	html:tag('div')
		:addClass('infobox__label')
		:wikitext(data['label'])

	html:tag('poem')
		:addClass('infobox__content')
		:wikitext(data['content'])

	return tostring(html)
end

--- wrap the infobox into ready-to-use HTML
---
--- @param title string text used as the header
--- @return string
function methodtable.renderInfobox(self, title, open)
	-- input validation
	checkType('Module:InfoboxNeue.renderInfobox', 1, self, 'table')
	checkType('Module:InfoboxNeue.renderInfobox', 2, title, 'string', true)
	checkType('Module:InfoboxNeue.renderInfobox', 3, open, 'boolean')

	local function renderContent()
		local html = mw.html.create('div')
			:addClass('infobox__content')
			:wikitext(table.concat(self.entries))
		return tostring(html)
	end

	local function renderHeader()
		local html = mw.html.create('div')
			:addClass('infobox__title')
			:wikitext(title)
		return tostring(html)
	end

	local frame = mw.getCurrentFrame()
	local output = getDetailsHTML({
		details = {
			class = 'infobox noexcerpt',
			content = renderContent(),
			open = open
		},
		summary = {
			class = 'infobox__header',
			content = renderHeader()
		}
	}, frame)

	return frame:extensionTag { name = 'templatestyles', args = { src = 'Module:InfoboxNeue/styles.css' } }
		.. output
end

--- New Instance
---
--- @return table InfoboxNeue
function InfoboxNeue.new()
	local instance = {
		entries = {}
	}

	setmetatable(instance, metatable)

	return instance
end

--- create an infobox from the given arguments
---
--- @param frame table
--- @return string
function InfoboxNeue.fromArgs(frame)
	-- setup
	local instance = InfoboxNeue:new()
	local args = require('Module:Arguments').getArgs(frame)

	-- section tracking
	local sections = {}
	local sectionMap = {}
	local currentSection

	-- image rendering
	if args['image'] then
		-- backwards compatibility: render a single image
		instance:renderImage(args['image'], args['caption'])
	else
		for i = 1, 10, 1 do
			if args['image' .. i] then
				-- render image1 through image3
				instance:renderImage(args['image' .. i], args['caption' .. i])
			end
		end
	end

	-- for each argument (ordered alphabetically)
	for key, value in orderedPairs(args) do
		-- if it's a label
		if type(string.match(key, '^label')) ~= 'nil' then
			-- if there's a section with this label's number
			if args['section' .. removeprefix(key, 'label')] then
				-- set the current section
				currentSection = args['section' .. removeprefix(key, 'label')]

				-- insert a new section
				table.insert(sections, {
					title = currentSection,
					col = args['section-col' .. removeprefix(key, 'label')] or 2,
					row = args['section-row' .. removeprefix(key, 'label')] or false,
					content = {}
				})

				-- map the section name to the section number
				sectionMap[currentSection] = #sections
			end

			-- set up the data for the item
			local data = {
				label = value,
				content = args['content' .. removeprefix(key, 'label')],
				colspan = args['colspan' .. removeprefix(key, 'label')]
			}

			local renderedItem = instance:renderItem(data)
			if renderedItem ~= '' then
				-- add to the section's content
				table.insert(sections[sectionMap[currentSection]].content, renderedItem)
			end
		end
	end

	-- for each section
	for _, section in ipairs(sections) do
		-- render it
		instance:renderSection(section)
	end

	-- render the entire infobox and return
	return instance:renderInfobox(
		args['title'] or mw.ext.displaytitle.get(mw.title.getCurrentTitle().fullText),
		yesno(args['open'] or true)
	)
end

return InfoboxNeue