Module:Infobox

local p = {} local namedArgs = {}

-- Translation note: Translatable strings are enclosed [=[ this sort ]=] of delimiter.

-- This variable holds the list of item types. local itemTypes = { armor = "Armor", block = "Solid Block", sblock = "Solid Block", oblock = "Solid Block", tblock = "Transparent Block", component = "Component", crop = "Crop", entity = "Entity", tentity = "Tile Entity", food = "Food", item = "Item", machine = "Machine", mob = "Monster", hmob = "Hostile Monster", nmob = "Neutral Monster", fmob = "Friendly Monster", seed = "Seed", tool = "Tool", wand = "Wand", weap = "Weapon", weapon = "Weapon", hull = "Steve's Carts Hull", module = "Steve's Carts Module", upgrade = "Steve's Carts Upgrade", landmark = "Landmark", ritual = "Ritual", liquid = "Liquid", ["multiblock structure"] = "Multiblock Structure", enchant = "Enchantment", mechanic = "Mechanic", dimension = "Dimension", flower = "Flower", command = "Command" }

-- This variable holds the shape of the infobox. -- It is a list of sections. -- Each section has a name (shown in documentation), a title (shown in the infobox), -- and then a list of subsections. Sections with consecutive names are merged in the docs. -- -- Each subsection is a list of rows. -- Each row is a list of one or two strings. -- A row with only one entry is made into a single cell with rowspan=2 -- Otherwise the first entry is bolded and the second is not. -- -- If instead of a string you use a table, it is interpreted as referring to a named argument -- given to the invocation. -- use =string to denote what argument to use, where type is image,imageIcon,text. -- Use desc for the text to be displayed in documentation.

local infoboxFormat = { {       name = "Basic", title = {arg="name", desc="The title that will be displayed at the top of the info box."}, {           { {arg={"blockimage","image"}, type="image", desc="The big picture. Use the complete filename, like \"Block_Sand.png\"." } },           { {arg={"iconimage","imageicon"}, desc="The icon image. Use Template:Gc or Template:O."} } },       {               { "Mod", {arg="mod", desc="The mod the item belongs to. Always refer to the main page of the mod. Please do not use version numbers like \"Industrial Craft 2\" or \"RedPower 2\"; just use the regular name instead. (Will detect page language and link to respective languages)"} }, { "Type", {arg="type", type="switch", allowedValues=itemTypes, desc="The item type."} }, { "Lore", {arg="lore", desc="Additional information that is displayed on the tooltip of the item in game. If the in-game tooltip has custom colors, they should be added to this parameter using the template. Colors that are typically used can be found on the Minecraft Wiki." }},           { "Module", {arg="module", desc="The module or modules that are required to be installed/enabled for the item to be present in the mod. Some examples of mods that add modules include RedPower 2, Project Red, and Railcraft."}} }   },    {        name="Basic", title="Properties", {           {"Blast Resistance", {arg="blastresistance", desc="The blast resistance of the block. Include only the number."} }, {"Hardness", {arg="hardness", desc="The hardness of a block." }}       }    }   }

local function table_print (tt, indent, done) done = done or {} indent = indent or 0 if type(tt) == "table" then local sb = {} for key, value in pairs (tt) do     table.insert(sb, string.rep (" ", indent)) -- indent it      if type (value) == "table" and not done [value] then done [value] = true table.insert(sb, "{\n"); table.insert(sb, table_print (value, indent + 2, done)) table.insert(sb, string.rep (" ", indent)) -- indent it       table.insert(sb, "}\n"); elseif "number" == type(key) then table.insert(sb, string.format("\"%s\"\n", tostring(value))) else table.insert(sb, string.format( "%s = \"%s\"\n", tostring (key), tostring(value))) end end return table.concat(sb) else return tt .. "\n" end end

local function to_string( tbl ) if "nil"       == type( tbl ) then return tostring(nil) elseif "table" == type( tbl ) then return table_print(tbl) elseif "string" == type( tbl ) then return tbl else return tostring(tbl) end end

local function trim(s) -- from PiL2 20.4 return (s:gsub("^%s*(.-)%s*$", "%1")) end

local function __genOrderedIndex( t ) local orderedIndex = {} for key in pairs(t) do       table.insert( orderedIndex, key ) end table.sort( orderedIndex, function(left, right)       if type(left) == "table" then            left = left[1]        end        if type(right) == "table" then            right = right[1]        end        return left < right    end) return orderedIndex end

local function orderedNext(t, state) -- Equivalent of the next function, but returns the keys in the alphabetic -- order. We use a temporary ordered key table that is stored in the -- table being iterated.

--print("orderedNext: state = "..tostring(state) ) if state == nil then -- the first time, generate the index t.__orderedIndex = __genOrderedIndex( t ) key = t.__orderedIndex[1] return key, t[key] end -- fetch the next value key = nil for i = 1,table.getn(t.__orderedIndex) do       if t.__orderedIndex[i] == state then key = t.__orderedIndex[i+1] end end

if key then return key, t[key] end

-- no more value to return, cleanup t.__orderedIndex = nil return end

local function orderedPairs(t) -- Equivalent of the pairs function on tables. Allows to iterate -- in order return orderedNext, t, nil end

-- Using the given frame as context, produce the string required for argdata. local function resolveArgument(frame, argdata) if not argdata then return nil end if type(argdata) == "string" then return argdata end

local text = frame.args[argdata.arg] if type(argdata.arg) == "table" then for i,name in ipairs(argdata.arg) do           if not text then text = frame.args[name] end end end if (not text) then return nil end

text = trim(text) if (text == "") then return nil end if not argdata.type then return text elseif argdata.type == "text" then return text elseif argdata.type=="image" then return '' elseif argdata.type=="switch" then local data = argdata.allowedValues[text] if string.find(text, "") then return text elseif (not data) then -- TODO: Only in main! return '??? '       else return data end end end

local function resolveArgumentCanonicalName(argdata) if not argdata then return nil end if type(argdata.arg) == "table" then return argdata.arg[1] else return argdata.arg end end

local function curryResolveArg(frame) return function(argdata) return resolveArgument(frame, argdata) end end

local function renderInfobox(effectiveBox, class) result = {} local out = function(thing) result[#result+1] = thing end out(" ") return table.concat(result, "") end

function p.infobox(frame) local resolveArg = curryResolveArg(frame) effectiveBox = {} for i,section in ipairs(infoboxFormat) do       local newSection = { title = resolveArg(section.title)} for j,subsection in ipairs(section) do           local newSubsection = {} for k,row in ipairs(subsection) do               local newRow = { resolveArg(row[1]), resolveArg(row[2])} if (#row == #newRow) then newSubsection[#newSubsection+1] = newRow end end if not (#newSubsection == 0) then newSection[#newSection+1] = newSubsection end end if not (#newSection == 0) then effectiveBox[#effectiveBox+1] = newSection end end --return table_print(effectiveBox) return renderInfobox(effectiveBox, frame.args.class) end

local function parameterList parameters = {} for i,section in ipairs(infoboxFormat) do       if type(section.title) == "table" then parameters[#parameters + 1] = section.title end for j,subsection in ipairs(section) do           for k,row in ipairs(subsection) do                for m,cell in ipairs(row) do                    if type(cell) == "table" then parameters[#parameters + 1] = cell end end end end end return parameters end

function p.templateCall(frame) local function resolveArgumentTemplateParameters(argdata) if not argdata then return "" end if type(argdata.arg) == "string" then return argdata.arg else local result = "" for i,name in ipairs(argdata.arg) do               result = result .."" end return result end end

local parameters = parameterList result = "" return result end

function p.parameterHelp(frame) local parameters = {} for i,section in ipairs(infoboxFormat) do       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

for j,subsection in ipairs(section) do           for k,row in ipairs(subsection) do                for m,cell in ipairs(row) do                    if type(cell) == "table" then sectiondata[#sectiondata+1] = cell end end end end end 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       result = result .. "===" .. section.name .. "===\n" for j,parameter in ipairs(section) do           local namelist = parameter.arg if type(parameter.arg) == "table" then namelist = table.concat(parameter.arg, " or ") end result = result .. "* '''" .. namelist .. "''': " .. parameter.desc if parameter.type == "switch" then result = result .. " Can be any of the following. Anything else will add the page to a list of pages with type errors.\n" for key,value in orderedPairs(collapseByValue(parameter.allowedValues)) do                   result = result .. "** " .. table.concat(key, " or ''") .. "'': " .. value .. "\n" end else result = result .. "\n" end end end

return result end

function p.emptyBox(frame) local parameters = parameterList local arglist = {class="infoboxNoCollapse"} for i,parameter in ipairs(parameters) do       local cname = resolveArgumentCanonicalName(parameter) arglist[cname] = "" end return p.infobox({args=arglist}) end

return p