Module:Infobox

local p = {}

local util = require("Module:Utility_functions")

local strings = require("Module:Infobox/strings")

local function makePreprocess(frame) return function(str) if str then return frame:preprocess(tostring(str)) or "wat" else return "" end end end

-- load an infobox local function loadInfobox(name) local moduleName = "Module:Infobox/" .. util.trim(name) .. util.pageSuffix local mod = nil pcall(function       mod = require(moduleName)    end) return mod end

-- return the canonical, ie first, name given in an argdata. local function argCName(argdata) if type(argdata.arg) == "string" then return argdata.arg else return argdata.arg[1] end end

-- wrap an argument value in the necessary surround local function wrapArg(argdata, value) if argdata.type == "image" then local sizeRequested = tonumber(argdata.maxSize or "0") if sizeRequested == 0 then sizeRequested = tonumber(strings.maxImageSize) end local parameters = (argdata.imageParameters or strings.imageParameters) if parameters == "" then parameters = nil end mw.log({'File:'.. value, sizeRequested .. 'px', parameters}) -- force frameless for now, because that makes size do the right thing local stuff = util.compact({'File:'.. value, sizeRequested .. 'px', parameters, "frameless"}) return '")..'' else return value end end

-- resolve argument data for a given arg table local function argtableResolver(argdata, argtable) if not argdata then return nil end if type(argdata) == "string" then return argdata end

local text = nil if type(argdata.arg) == "string" then text = argtable[argdata.arg] else for i,arg in ipairs(argdata.arg) do           local candidate = util.trim(argtable[arg] or "") if not (candidate == "") then text = candidate end end end if not text then return nil end if argdata.type == "switch" then if not argdata.allowedValues[text] then text = strings.unknownType .. ' '       else text = argdata.allowedValues[text] end end if argdata.style then return {style=argdata.style, wrapArg(text)} else return wrapArg(argdata, text) end end

local function makeResolver(frame) if frame.args.fromParent then frame = frame:getParent end return function(argdata) return argtableResolver(argdata, frame.args) end end

-- resolve argument data for an empty infobox -- parameters --  argdata: argdata|string -- returns --  string|{style=cellstyle, string} local function emptyBoxResolver(argdata) if not argdata then return "wat" end if type(argdata) == "string" then return argdata elseif type(argdata) == "table" then local text = "" if argdata.style then return {style=argdata.style, wrapArg(argdata, text)} else return wrapArg(argdata, text) end end end

-- resolve the arguments of an infobox definition according to the given resolver, -- and return a structure with all the arguments resolved and any unnecessary sections -- etc omitted (sections with an argument title are never omitted). -- parameters --  args.box: infobox definition --  args.resolver: function(argdata|string) -> string. local function calculateEffectiveInfobox(args) local effectiveBox = {type="box"} for i, section in ipairs(args.box) do       if not section.docOnly then local newSection = { title = args.resolver(section.title), type="section" } for j, subsection in ipairs(section) do           if not subsection.docOnly then local newSubsection = {type="subsection"} for k, row in ipairs(subsection) do               if not row.docOnly then local newRow = { type="row"} for m, cell in ipairs(row) do                   newRow[#newRow + 1] = args.resolver(cell) end if (#row == #newRow) then newSubsection[#newSubsection+1] = newRow end end end if (#newSubsection > 0) then newSection[#newSection + 1] = newSubsection end end end if (#newSection > 0) or (type(section.title) == "table") then effectiveBox[#effectiveBox + 1] = newSection end end end return effectiveBox end

-- convert an infobox definition that has had all the arguments resolved, to a string. -- box like -- { class=tableclass, { title=sectionheader, { { cell, cell }, {cell, cell } } } } -- cell like string or { style=cellstyle, content } local function renderBox(box, wtprocessor) result = "" local out = function(thing) result = result .. thing end out(' ') return result end

local function renderEmptyBox(box) local presentedBox = calculateEffectiveInfobox{box=box.format, resolver = emptyBoxResolver} presentedBox.class="infoboxNoCollapse" return renderBox(presentedBox, function(str)       return str    end) end

function p.calculateParameterListing(box) local parameters = {} for i,section in ipairs(box) do       if not section.noDoc then local sectiondata = { name = section.name} if (not parameters[#parameters]) or (not (sectiondata.name == parameters[#parameters].name)) then parameters[#parameters+1] = sectiondata else sectiondata = parameters[#parameters] end if type(section.title) == "table" then sectiondata[#sectiondata+1] = section.title end if section.desc then sectiondata[#sectiondata+1] = { docText = true, text = section.desc } end

for j,subsection in ipairs(section) do               if not subsection.noDoc then if subsection.desc then sectiondata[#sectiondata+1] = { docText = true, text = section.desc } end for k,row in ipairs(subsection) do                       if not subsection.noDoc then if row.desc then sectiondata[#sectiondata+1] = { docText = true, text = section.desc } end for m,cell in ipairs(row) do                               if (type(cell) == "table") and (cell.desc) and (not cell.noDoc) then sectiondata[#sectiondata+1] = cell end end end end end end end end return parameters end

local function renderParameterListing(box) local parameters = p.calculateParameterListing(box) local collapseByValue = function(tab) local iv={} for k,v in pairs(tab) do           if not iv[v] then iv[v]={k} else iv[v][#(iv[v]) + 1] = k           end end local s={} for k,v in pairs(iv) do           if not s[v] then s[v]=k end end return s   end

result = '' for i,section in ipairs(parameters) do       if section.name then result = result .. "===" .. section.name .. "===\n" end for j,parameter in ipairs(section) do           if parameter.docText then result = result .. parameter.text .. '\n' else local namelist = parameter.arg if type(parameter.arg) == "table" then namelist = table.concat(parameter.arg, " " .. strings.nameorname .. " ") end result = result .. "* '''" .. namelist .. "''': " .. parameter.desc if parameter.type == "switch" then result = result .. " " .. strings.switchdoc .."\n" for key,value in orderedPairs(collapseByValue(parameter.allowedValues)) do                       result = result .. "** " .. table.concat(key, "" .. strings.nameorname .. "''") .. "'': " .. value .. "\n" end else result = result .. "\n" end end end end return result end

-- emit an infobox. -- parameters: --  1: Infobox definition to use --  everything else: whatever the infobox definition says function p.infobox(frame) local box = loadInfobox(frame.args[1]) if not box then return "!!! " .. frame.args[1] .. strings.loadFailure .. " !!!"   end

local presentedBox = calculateEffectiveInfobox{box=box.format, resolver = makeResolver(frame)} return renderBox(presentedBox, function(str)       return frame:preprocess(str)    end) end

-- emit an infobox with the parameters displayed -- parameters: --  1: Infobox definition to use function p.emptyBox(frame) local box = loadInfobox(frame.args[1]) if not box then return "!!! " .. strings.loadFailure .. " " .. frame.args[1] .. " !!!"   end return renderEmptyBox(box) end

-- emit the parameter list for documentation -- parameters --  1: Infobox definition to use function p.parameterHelp(frame) local box = loadInfobox(frame.args[1]) if not box then return "!!! " .. strings.loadFailure .. " " .. frame.args[1] .. " !!!"   end

return renderParameterListing(box.format) end

-- emit the entire documentation page -- parameters --  1: infobox definition to use -- in the infobox definition --  intro is the text at the top --  postParameterText is displayed after the parameter listing --  examples is an array of examples: { { optional text = wikitext string, template-supporting wikitext}, ... } function p.documentationPage(frame) local box = loadInfobox(frame.args[1]) if not box then return "!!! " .. strings.loadFailure .. " " .. frame.args[1] .. " !!!"   end local fp = makePreprocess(frame) local function interp(s, tab) return (s:gsub('(${%b})', function(w) return tab[w:sub(3, -2)] or w end)) end local function hatnote(str) frame:expandTemplate{title = "Hatnote", args = {str}} end out = frame:expandTemplate{title = "Doc/Start"} local noteText = interp(strings.generatedFrom, {template = "Module:Infobox/" .. frame.args[1]}) out = out .. fp(noteText) out = out .. fp(strings.docLead) out = out .. fp(box.docLead) out = out .. "==" .. strings.parameters .. "==\n" out = out .. fp(strings.parameterLead) out = out .. fp(box.parameterLead) out = out .. renderEmptyBox(box) out = out .. renderParameterListing(box) out = out .. '\n' .. fp(box.parameterTrail) out = out .. "\n==" .. strings.examples .. "==\n" for i, example in ipairs(box.examples) do       out = out .. fp(example.text) .. '\n' out = out .. ' \n\n' end out = out .. frame:expandTemplate{title = "Doc/End"} return out end

return p