Die Dokumentation für dieses Modul kann unter Modul:Infobox/Doku erstellt werden
local Infobox = {} local metatable = {} local methodtable = {} metatable.__index = methodtable metatable.__tostring = function( self ) return tostring( self:getBoxText() ) end -- Uses capiunto as the base local capiunto = require 'capiunto' local libraryUtil = require( 'libraryUtil' ) --- Returns true if input is a string or number --- @param input string|number|table --- @return boolean local function verifyStringNum( input ) return type( input ) == 'string' or type( input ) == 'number' end --- This fills the box with content local function addBoxContent( self ) if self.contentAdded then return end for _, row in ipairs( self.rows ) do local label = tostring( row.label ) if row.type == 'row' then if (type( label ) == 'string' or type( label ) == 'number') and ( type( row.data ) == 'string' or type( row.data ) == 'number' ) then self.capiunto:addRow( label, row.data, row.class, row.rowClass, row.attributes ) end elseif row.type == 'header' or row.type == 'image' then if row.type == 'image' and row.rowClass ~= 'row-image' then row.rowClass = row.rowClass .. ' row-image' end -- Only add the header if it has content if mw.ustring.find( row.rowClass, 'row-header', 1, true ) ~= nil and self.headerContentCounts[ label ] ~= nil and self.headerContentCounts[ label ] == 0 then -- skip add else self.capiunto:addRow( nil, label, row.class, row.rowClass, row.attributes ) end elseif row.type == 'subheader' then self.capiunto:addSubHeader( label, row.class, row.style, row.attributes ) end end self.contentAdded = true end --- Iterate table in key order local function spairs( self, orderFn ) -- collect the keys local keys = {} for k, v in pairs( self ) do if v ~= nil then keys[ #keys + 1 ] = tostring( k ) end end -- if order function given, sort by it by passing the table and keys a, b, -- otherwise just sort the keys if orderFn then table.sort( keys, function( a, b ) return orderFn( self, a, b ) end ) else table.sort( keys ) end -- return the iterator function local i = 0 return function() i = i + 1 if keys[ i ] then return keys[ i ], self[ keys[ i ] ] end end end --- Parse all available capiunto options from a table --- Returns only non nil values --- --- @param args table --- @return table local function parseArgs( args ) local options = { [ 'isChild' ] = args[ 'isChild' ] or nil, [ 'isSubbox' ] = args[ 'isSubbox' ] or nil, [ 'title' ] = args[ 'title' ] or nil, [ 'titleClass' ] = args[ 'titleClass' ] or nil, [ 'titleStyle' ] = args[ 'titleStyle' ] or nil, [ 'top' ] = args[ 'top' ] or nil, [ 'topClass' ] = args[ 'topClass' ] or nil, [ 'topStyle' ] = args[ 'topStyle' ] or nil, [ 'bottom' ] = args[ 'bottom' ] or nil, [ 'bottomClass' ] = args[ 'bottomClass' ] or nil, [ 'bottomStyle' ] = args[ 'bottomStyle' ] or nil, [ 'captionStyle' ] = args[ 'captionStyle' ] or nil, [ 'imageStyle' ] = args[ 'imageStyle' ] or nil, [ 'imageClass' ] = args[ 'imageClass' ] or nil, [ 'bodyClass' ] = args[ 'bodyClass' ] or nil, [ 'bodyStyle' ] = args[ 'bodyStyle' ] or nil, [ 'headerStyle' ] = args[ 'headerStyle' ] or nil, [ 'labelStyle' ] = args[ 'labelStyle' ] or nil, [ 'dataStyle' ] = args[ 'dataStyle' ] or nil, [ 'tableAttributes' ] = args[ 'tableAttributes' ] or nil, } local i = 0 local keys, values = {}, {} for k, v in pairs( options ) do i = i + 1 keys[ i ] = k values[ i ] = v end while i > 0 do if keys[ i ] == nil then table.remove( keys, i ) table.remove( values, i ) break end i = i - 1 end local finalOptions = {} for i = 1, #keys do finalOptions[ keys[ i ] ] = values[ i ] end if type( finalOptions.bodyClass ) == 'string' then finalOptions.bodyClass = finalOptions.bodyClass .. ' infobox' else finalOptions.bodyClass = 'infobox' end local finalArgs = {} for k, v in pairs( args ) do if finalOptions[ k ] == nil and v ~= nil then finalArgs[ k ] = v end end return finalOptions, finalArgs end --- Replaces a row if labels match and returns the replaced index --- Otherwise returns the next free index --- --- @return number New index local function replaceInRows( self, targetLabel ) for idx, row in ipairs( self.rows ) do if row.label == targetLabel then table.remove( self.rows, idx ) return idx end end return #self.rows + 1 end --- Adds one of three special rows to the infobox --- As defined by Capiunto --- Available rowTypes are: title (caption), top (first row), bottom (last row) --- --- @param self table The infobox --- @param rowType string title, top or bottom --- @param class string Row class --- @param style string Css style --- @return table local function addSpecialRow( self, rowType, text, class, style ) if self.capiunto == nil then return end if verifyStringNum( text ) then self.capiunto.args[ rowType ] = text end if type( class ) == 'string' then self.capiunto.args[ rowType .. 'Class' ] = class end if type( style ) == 'string' then self.capiunto.args[ rowType .. 'Style' ] = style end return self end --- Generates a link to Special:Upload with pre-defined parameters --- --- @param fileName string|nil The filename including the extension --- @param title string The title of the infobox local function generateUploadLink( fileName, category ) local title = mw.title.new( 'Special:Upload' ) if fileName == nil then fileName = mw.title.getCurrentTitle().text .. '.jpg' end if category == nil then category = mw.title.getCurrentTitle().text end local description = [==[ =={{int:filedesc}}== {{Information |description={{de|1=%s}} |date=%s |source=<!-- BITTE LINK ZU BILD EINFÜGEN --> |author=RSI |permission= |other versions= }} =={{int:license-header}}== {{license-rsi}} [[Kategorie:%s]] ]==] description = mw.ustring.format( description, category, mw.getContentLanguage():formatDate('Y-m-d H:i:s'), category ) local parameters = { [ 'wpDestFile' ] = fileName, [ 'wpLicense' ] = 'license-rsi', [ 'wpUploadDescription' ] = description } return title:fullUrl( parameters, 'https' ) end --- Generates a link to Special:Uploadwizard with pre-defined parameters --- --- @param fileName string|nil The filename excluding the extension --- @param title string The title of the infobox local function generateUploadwizardLink( fileName, category ) local title = mw.title.new( 'Special:Uploadwizard' ) if fileName == nil then fileName = mw.title.getCurrentTitle().text end if category == nil then category = mw.title.getCurrentTitle().text end local parameters = { [ 'categories' ] = category, [ 'title' ] = fileName, [ 'description' ] = fileName, -- Missing: Date -- Missing pre license select } return title:fullUrl( parameters, 'https' ) end --- Base method to add a row --- @see https://www.mediawiki.org/wiki/Extension:Capiunto/Infobox --- @param self table The instance --- @param rowLabel string Row label --- @param rowData string Row content (can be anything) --- @param dataClass string CSS class added to data --- @param rowCssClass string CSS class added to row --- @param rowStyle string CSS style only used if type is 'subheader'! --- @param type string Either 'row', 'header', 'image' or 'subheader' --- @param attributes table|nil Optional attributes that are added to the rows function methodtable.addRow( self, rowLabel, rowData, dataClass, rowCssClass, rowStyle, type, attributes ) self.checkSelf( self, 'addRow' ) type = type or 'row' if self.removeEmpty == true then if type == 'row' and ( rowData == nil or rowData == self.emptyString ) then return self end end if type == 'header' then self.currentHeader = rowLabel if self.headerContentCounts[ rowLabel ] == nil then local count = 0 -- A "single" header is a header without content -- This is used to circumvent removing the header as it as no content if dataClass == 'single' then count = 1 end self.headerContentCounts[ rowLabel ] = count end end -- increment the header count if self.currentHeader ~= nil and type == 'row' and self.headerContentCounts[ self.currentHeader ] ~= nil then self.headerContentCounts[ self.currentHeader ] = self.headerContentCounts[ self.currentHeader ] + 1 end local pos = -1 if self.allowReplace == true and #self.rows > 0 then pos = replaceInRows( self, rowLabel ) else self.rowCount = self.rowCount + 1 pos = self.rowCount end if rowCssClass == nil then rowCssClass = 'row-' .. type if rowCssClass == 'row-row' then rowCssClass = 'row' end end if not mw.ustring.match( rowCssClass, 'row' ) then local toAdd = type if toAdd ~= 'row' then toAdd = 'row-' .. type end rowCssClass = rowCssClass .. ' ' .. toAdd end table.insert( self.rows, pos, { type = type, label = rowLabel, data = rowData or nil, class = dataClass or nil, rowClass = rowCssClass, style = rowStyle or nil, attributes = attributes or nil, }) return self end --- Adds a header to the infobox function methodtable.addHeader( self, text, class, rowClass, attributes ) self.checkSelf( self, 'addHeader' ) rowClass = ( rowClass or '' ) .. ' row-header' return self:addRow( text, nil, class, rowClass, nil, 'header', attributes ) end --- Adds a title row to the infobox function methodtable.addTitle( self, text, class, rowClass, attributes ) self.checkSelf( self, 'addTitle' ) rowClass = ( rowClass or '' ) .. ' row-title' return self:addRow( text, nil, class, rowClass, nil, 'header', attributes ) end --- Adds a subheader to the infobox function methodtable.addSubHeader( self, text, class, style, attributes ) self.checkSelf( self, 'addSubHeader' ) return self:addRow( text, nil, class, 'row-subheader', style, 'subheader', attributes ) end --- Adds a caption to the infobox function methodtable.addCaption( self, text, class, style ) self.checkSelf( self, 'addCaption' ) return addSpecialRow( self, 'title', text, class, style ) end --- Adds a caption to the infobox function methodtable.addTop( self, text, class, style ) self.checkSelf( self, 'addTop' ) return addSpecialRow( self, 'top', text, class, style ) end --- Adds a bottom to the infobox function methodtable.addBottom( self, text, class, style ) self.checkSelf( self, 'addBottom' ) return addSpecialRow( self, 'bottom', text, class, style ) end --- Adds an image to the infobox --- @param file string Wiki page filename --- @param options table Image options --- @param checkExistence boolean True to check if the file exists --- @param attributes table|nil Optional attributes function methodtable.addImage( self, file, options, checkExistence, attributes ) self.checkSelf( self, 'addImage' ) local isPlaceholder = false if type( file ) ~= 'string' then if self.displayPlaceholder == false then return end file = self.placeholderImage isPlaceholder = true end local exists = true local title = mw.title.new( mw.uri.decode( file, 'WIKI' ), 6 ) if title == nil then return end if type( options ) == 'string' then options = { [ 'rowClass' ] = options } else options = options or {} end if checkExistence ~= nil and checkExistence == true then exists = title.exists end local class = options[ 'rowClass' ] or nil local function buildOptions( imageOptions ) local out = {} for k, v in pairs( imageOptions ) do if k ~= 'rowClass' and type( k ) == 'string' and type( v ) == 'string' then table.insert( out, k .. '=' .. v ) elseif type( k ) == 'number' then table.insert( out, v ) end end return table.concat( out, '|' ) end if isPlaceholder == true then options[ 'link' ] = generateUploadLink() if class == nil then class = 'placeholder' else class = class .. ' placeholder' end end local imageOptions = buildOptions( options ) if imageOptions ~= '' then imageOptions = '|' .. imageOptions if isPlaceholder == true then imageOptions = imageOptions .. '|Klicke um Bild hochzuladen' end end local header = '[[' .. title.prefixedText .. imageOptions .. ']]' if exists == false then header = 'Datei fehlt' end return self:addRow( header, nil, class, 'row-image', nil, 'image', attributes ) end --- Allows to add arbitrary rows from template args --- @param args table Template arguments --- @param prefix string|nil An optional prefix that each argument must have to be added as a row --- @param allowedKeys table|nil An optional table of keys that are allowed as rows, are passed to ustring.match --- --- Example param = !row -> Only Template arguments in the form of |!row...=Content are added as Rows --- The prefix gets replaced from each key: |!rowLabel=Content => Label=Content --- --- The row type can be set by adding 'subheader' or 'header' to the key --- Example: |header1=Content => Header row with content 'content' added --- Multiple headers must be suffixed by numbers --- --- !!! NOTE !!! --- Lua tables do not preserve order, to mitigate this, this module SORTS the given arguments ALPHABETICALLY --- You can prefix arguments with numbers to preserve the order --- Example Template: --- {{Infobox --- |1-header=Header for block one --- |1.1-RowLabel=This is the content for row 1.1 --- |1.2-RowLabel=This again is the content --- |2-subheader=Subheader --- |2.1-Label=Content --- |2.2... --- }} --- --- Example Module Call: --- infobox.addRowsFromArgs({ --- [ '1-header' ] = 'Header for block one', --- [ '1.1-RowLabel' ] = 'This is...', --- }) function methodtable.addRowsFromArgs( self, args, prefix, allowedKeys ) self.checkSelf( self, 'addRowsFromArgs' ) if type( args ) ~= 'table' then return end local _, args = parseArgs( args ) local function canAdd( key ) if key == 'image' then -- Image is added separately return false end if prefix == nil then if type( allowedKeys ) == 'table' then for _, allowed in pairs( allowedKeys ) do if mw.ustring.match( key, allowed ) then return true end end return false end return true elseif type( prefix ) == 'string' then return mw.ustring.match( key, prefix ) end return true end for k, v in spairs( args ) do if type( k ) == 'string' and type( v ) == 'string' then if canAdd( k ) then if prefix~= nil and type( prefix ) == 'string' then k = mw.ustring.gsub( k, prefix, '' ) end -- Remove Digits - and . at start of label k = mw.ustring.gsub( k, '^[%d%-%.]+', '' ) -- Removes digits at the end, should enable MW Infobox behaviour k = mw.ustring.gsub( k, '[%d]+$', '' ) local splitted = mw.text.split( v, '<>', true ) local label = splitted[1] local class = splitted[2] or '' local rowClass = splitted[3] or '' if mw.ustring.match( k, 'subheader' ) then self:addSubHeader( v ) elseif mw.ustring.match( k, 'header' ) then self:addHeader( label, class, rowClass ) elseif mw.ustring.match( k, 'title' ) then self:addTitle( label, class, rowClass ) elseif mw.ustring.match( k, 'caption' ) then self:addCaption( label, class, rowClass ) elseif mw.ustring.match( k, 'top' ) then self:addTop( label, class, rowClass ) elseif mw.ustring.match( k, 'bottom' ) then self:addBottom( label, class, rowClass ) else self:addRow( k, label, class, rowClass ) end end end end return self end --- Flag to allow or disable row replacing --- @param flag boolean function methodtable.setAllowReplace( self, flag ) self.checkSelf( self, 'setAllowReplace' ) self.allowReplace = flag end --- Returns the raw capiunto box --- @return table function methodtable.getBox( self ) self.checkSelf( self, 'getBox' ) addBoxContent( self ) return self.capiunto end --- Returns the table string --- @return string function methodtable.getBoxText( self ) self.checkSelf( self, 'getBoxText' ) return tostring( self:getBox() ) end --- Init the infobox --- @param options table|nil Option table passed to capiunto.create function Infobox.create( options ) local instance = { -- Table containing Header = count of rows headerContentCounts = {}, -- The currently active header currentHeader = nil, -- The row tables rows = {}, -- Total number of rows in the box rowCount = 0, -- Capiunto table capiunto = {}, -- Flag to stop adding rows to the box contentAdded = false, -- Flag to enable replacing already added rows allowReplace = false, -- 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', } if options.allowReplace ~= nil then instance.allowReplace = options.allowReplace options.allowReplace = nil end if options.removeEmpty ~= nil then instance.removeEmpty = true options.removeEmpty = nil end if options.emptyString ~= nil then instance.emptyString = options.emptyString options.emptyString = nil end if options.displayPlaceholder ~= nil then instance.displayPlaceholder = options.displayPlaceholder options.displayPlaceholder = nil end if options.placeholderImage ~= nil then instance.placeholderImage = options.placeholderImage options.placeholderImage = nil end setmetatable( instance, metatable ) instance.capiunto = capiunto.create( parseArgs( options or {} ) ) instance.checkSelf = libraryUtil.makeCheckSelfFunction( 'Infobox', 'instance', instance, 'Method Call' ) return instance end --- Create a infobox from args --- @param frame table --- @return string function Infobox.fromArgs( frame ) local arguments = require( 'Module:Arguments' ).getArgs( frame ) local options, args = parseArgs( arguments ) if options.bodyClass == nil then options.bodyClass = 'floatright' end local box = Infobox.create( options ) box:addImage( arguments.image,{ 'frameless', '600px' } ) if arguments.image ~= nil then arguments.image = nil end box:addRowsFromArgs( args ) return tostring( box ) end return Infobox