Module:InfoboxNeue: Difference between revisions
From Amaranth Legacy, available at amaranth-legacy.community
More actions
Content deleted Content added
Tesinormed (talk | contribs) No edit summary Tag: Reverted |
Tesinormed (talk | contribs) m Removed protection from "Module:InfoboxNeue" |
||
| (224 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 |
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 |
metatable.__tostring = function(self) |
||
return tostring( |
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 = |
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 |
||
--- @ |
--- @param caption string |
||
function methodtable.renderImage( |
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 |
||
--- |
--- wrap the infobox section into ready-to-use HTML |
||
--- |
--- |
||
--- @param data table |
--- @param data table |
||
function methodtable.renderSection(self, data) |
|||
--- @return string html |
|||
-- input validation |
|||
function methodtable.renderIndicator( self, data ) |
|||
checkType( |
checkType('Module:InfoboxNeue.renderSection', 1, self, 'table') |
||
checkType( |
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 |
||
--- |
--- render an item in the infobox |
||
--- |
--- |
||
--- @param data table |
--- @param data table |
||
--- @return string |
--- @return string |
||
function methodtable. |
function methodtable.renderItem(self, data) |
||
-- input validation |
|||
checkType( 'Module:InfoboxNeue.renderHeader', 1, self, 'table' ) |
|||
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[ |
if data['spacebetween'] == true then html:addClass('infobox__grid--space-between') end |
||
if data[ |
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 |
|||
:addClass('infobox__label') |
|||
: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 |
|||
:addClass('infobox__content') |
|||
: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() |
|||
:wikitext( data[ key ] ) |
|||
if key == 'data' then |
|||
textWrapper:newline() |
|||
end |
|||
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 into ready-to-use HTML |
||
--- |
--- |
||
--- @param |
--- @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( |
checkType('Module:InfoboxNeue.renderInfobox', 1, self, 'table') |
||
checkType('Module:InfoboxNeue.renderInfobox', 2, title, 'string', true) |
|||
checkType( |
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( |
local html = mw.html.create('div') |
||
:addClass( |
:addClass('infobox__content') |
||
:wikitext(table.concat(self.entries)) |
|||
:node( innerHtml ) |
|||
return tostring( |
return tostring(html) |
||
end |
end |
||
local function |
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 = |
local frame = mw.getCurrentFrame() |
||
local output = getDetailsHTML( |
local output = getDetailsHTML({ |
||
details = { |
details = { |
||
class = 'infobox |
class = 'infobox noexcerpt', |
||
content = renderContent() |
content = renderContent(), |
||
open = open |
|||
}, |
}, |
||
summary = { |
summary = { |
||
class = ' |
class = 'infobox__header', |
||
content = |
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 606: | Line 205: | ||
--- |
--- |
||
--- @return table InfoboxNeue |
--- @return table InfoboxNeue |
||
function InfoboxNeue.new( |
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( |
setmetatable(instance, metatable) |
||
return instance |
return instance |
||
end |
end |
||
--- |
--- create an infobox from the given arguments |
||
--- |
--- |
||
--- @param frame table |
--- @param frame table |
||
--- @return string |
--- @return string |
||
function InfoboxNeue.fromArgs( |
function InfoboxNeue.fromArgs(frame) |
||
-- setup |
|||
local instance = InfoboxNeue:new() |
local instance = InfoboxNeue:new() |
||
local args = require( |
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 |
||
-- image rendering |
|||
if args['image'] then |
|||
-- backwards compatibility: render a single image |
|||
if args[ 'indicator' ] then |
|||
instance:renderImage(args['image'], args['caption']) |
|||
data = args[ 'indicator' ], |
|||
class = args[ 'indicatorClass' ] |
|||
} ) |
|||
end |
|||
else |
else |
||
for i = 1, |
for i = 1, 10, 1 do |
||
if args[ |
if args['image' .. i] then |
||
-- render image1 through image3 |
|||
instance:renderImage( args[ 'image' .. i ] ) |
|||
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 = |
label = value, |
||
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 |
-- for each section |
||
for _, section in ipairs(sections) do |
|||
instance:renderSection( { |
|||
-- render it |
|||
title = section.title, |
|||
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