Module:Infobox

-- TODO: Documentation generator -- TODO: Handle image widths correctly

local p = {}

local util = require("Module:Utility_functions")

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

-- load an infobox local function loadInfobox(name) local moduleName = "Module:Infobox/" .. util.trim(name) .. util.pageSuffix return require(moduleName) 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

-- Returns the size of an image. Uses an expensive function. local function getImageWidth(pageName) local title = mw.title.makeTitle("File", pageName) local filewidth = (title.file or {width="0"}).width return tonumber(filewidth or "0") end

-- wrap an argument value in the necessary surround local function wrapArg(argdata, value) if argdata.type == "image" then local sizeRequested = argdata.maxSize or strings.maxImageSize local parameters = argdata.imageParameters or strings.imageParameters local imageSize = getImageWidth(value) local sizeToUse = sizeRequested if imageSize < sizeToUse then sizeToUse = imageSize end 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       local newSection = { title = args.resolver(section.title), type="section" } for j, subsection in ipairs(section) do           local newSubsection = {type="subsection"} for k, row in ipairs(subsection) do               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 if (#newSubsection > 0) then newSection[#newSection + 1] = newSubsection end end if (#newSection > 0) or (type(section.title) == "table") then effectiveBox[#effectiveBox + 1] = newSection 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

-- 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

local presentedBox = calculateEffectiveInfobox{box=box.format, resolver = emptyBoxResolver} presentedBox.class="infoboxNoCollapse" return renderBox(presentedBox, function(str)       return str    end) 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

end

return p