Module:Infobox

local p = {}

local util = require("Module:Utility_functions")

function loadTranslatedModule(name) local moduleName = name .. util.pageSuffix local mod = nil pcall(function       mod = require(moduleName)    end) return mod end

local strings = loadTranslatedModule("Module:Infobox/strings") if not strings then strings = {} end setmetatable(strings, {__index = function(tab, key)   if (key == "_fellbackStrings") then        tab._fellbackStrings = {}        return tab._fellbackStrings    end    if (key == "_missingStrings") then        tab._missingStrings = {}        return tab._missingStrings    end    if key == "_ModuleStringTableProblem" then        if rawget(tab,"_stringTableEntirelyMissing") then            return "!!! Module:Infobox/strings is gone or not a valid Lua module !!!"        end        if (#tab._fellbackStrings > 0) or (#tab._missingStrings > 0) then            return "!!! Problem with Module:Infobox/strings or a translation thereof. Fallbacks used for: {" .. table.concat(tab._fellbackStrings, ", ") .. "}. Missing strings: {" .. table.concat(tab._missingStrings, ", ") .. "}. Please inform a translation admin as the translation markings probably need updating. !!!"        else            return nil end end if not rawget(tab,"_fallback") then pcall(function           tab._fallback = require("Module:Infobox/strings")        end) if not rawget(tab,"_fallback") then tab._stringTableEntirelyMissing = true tab._fallback = { } end end local val = tab._fallback[key] if val then tab._fellbackStrings[#(tab._fellbackStrings)+1] = key else tab._missingStrings[#(tab._missingStrings)+1] = key val = "noString!"..key end tab[key] = val -- inhibit repeats. return val end})

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) return loadTranslatedModule("Module:Infobox/" .. util.trim(name)) 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] = { text = section.desc } end

for j,subsection in ipairs(section) do               if not subsection.noDoc then if subsection.desc then sectiondata[#sectiondata+1] = { text = subsection.desc } end for k,row in ipairs(subsection) do                       if not subsection.noDoc then if row.desc then sectiondata[#sectiondata+1] = { text = row.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.text then result = result .. parameter.text .. '\n' elseif parameter.desc then 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 util.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

local function makeInvokeWithBox(func) return function(frame) local box = loadInfobox(frame.args[1]) if not box then out = "!!! " .. strings.loadFailure .. " Module:Infobox/" .. frame.args[1] .. util.pageSuffix "" .. strings.informTranslationAdmin .. " !!!"       else out = func(frame, box) end if strings._ModuleStringTableProblem then out = strings._ModuleStringTableProblem .. "\n\n" .. out end return out end end

-- emit an infobox. -- parameters: --  1: Infobox definition to use --  everything else: whatever the infobox definition says

p.infobox = makeInvokeWithBox(function(frame, box)   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 p.emptyBox = makeInvokeWithBox(function(frame, box)   return renderEmptyBox(box) end)

-- emit the parameter list for documentation -- parameters --  1: Infobox definition to use p.parameterHelp = makeInvokeWithBox(function(frame, box)   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}, ... } p.documentationPage = makeInvokeWithBox(function(frame, box)   local fp = makePreprocess(frame)    -- this is exactly as lua-users wiki defined it. Never mind the odd gsub argument.    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)        return 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 .. hatnote(fp(noteText)) .. '\n'    out = out .. fp(strings.docLead) .. ' '    out = out .. fp(box.docLead) .. '\n\n'    out = out .. "==" .. strings.parameters .. "==\n"    out = out .. fp(strings.parameterLead) .. " "    out = out .. fp(box.parameterLead)    out = out .. renderEmptyBox(box) .. '\n'    out = out .. renderParameterListing(box.format)    out = out .. '\n' .. fp(box.parameterTrail)    out = out .. ' '    out = out .. "\n==" .. strings.examples .. "==\n"    if box.examples then        for i, example in ipairs(box.examples) do            out = out .. fp(example.text) .. '\n'            out = out .. ' \n\n'        end    else        out = out .. hatnote(fp(strings.noexamples or "No examples!"))    end    out = out .. frame:expandTemplate{title = "Doc/End"}    return out end)

return p