Module:Infobox: Difference between revisions

From Official Factorio Wiki
Jump to navigation Jump to search
(Created infobox tabs with space age support, without expensive mode support)
(Added label_prefix parameter so that the space age icon doesnt get included in translations, don't show technology or item vrows if the argument is exactly "none")
Line 1: Line 1:
local util = require("Module:Util")
local parsing = require("Module:Infobox/parsing")
local p = {}
local p = {}


Line 6: Line 9:
   local sa = frame:expandTemplate{title = "Space age"} .. " "
   local sa = frame:expandTemplate{title = "Space age"} .. " "


   if not p._empty_arg(args["recipe"]) then
   if not util._empty_arg(args["recipe"]) then
     ret[#ret+1] = p._vrow(frame, {"Recipe",    args["recipe"], p._crafting_parsing(frame, args["recipe"])})
     ret[#ret+1] = p._vrow(frame, {"Recipe",    args["recipe"], parsing._crafting(frame, args["recipe"])})
     ret[#ret+1] = p._vrow(frame, {"Total raw", args["recipe"], p._crafting_raw(frame, p._arg_or(args["total-raw"], args["recipe"]))})
     ret[#ret+1] = p._vrow(frame, {"Total raw", args["recipe"], parsing._crafting_raw(frame, util._arg_or(args["total-raw"], args["recipe"]))})
   end
   end


   if not p._empty_arg(args["cost"]) then
   if not util._empty_arg(args["cost"]) then
     local cost = p._item_parsing(frame, args["cost"]) .. ((not p._empty_arg(args["cost-multiplier"])) and "✖ <big>" .. args["cost-multiplier"] .. "</big>" or "")
     local cost = parsing._item(frame, args["cost"]) .. ((not util._empty_arg(args["cost-multiplier"])) and "✖ <big>" .. args["cost-multiplier"] .. "</big>" or "")
     ret[#ret+1] = p._vrow(frame, {"Cost", args["cost"], cost})
     ret[#ret+1] = p._vrow(frame, {"Cost", args["cost"], cost})
   end
   end
Line 18: Line 21:
   ret[#ret+1] = p._extra    (frame,                          args["extra1"])
   ret[#ret+1] = p._extra    (frame,                          args["extra1"])
    
    
   if not p._empty_arg(args["map-color"]) then
   if not util._empty_arg(args["map-color"]) then
     local color = frame:expandTemplate{title = 'Color', args = {args["map-color"]}}
     local color = frame:expandTemplate{title = 'Color', args = {args["map-color"]}}
     ret[#ret+1] = p._row(frame, {"Map color", args["map-color"], color})
     ret[#ret+1] = p._row(frame, {"Map color", args["map-color"], color})
Line 29: Line 32:
   ret[#ret+1] = p._row_simple(frame, "Fluid storage volume",  args["fluid-storage-volume"])
   ret[#ret+1] = p._row_simple(frame, "Fluid storage volume",  args["fluid-storage-volume"])


   if not p._empty_arg(args["expected-resources"]) then
   if not util._empty_arg(args["expected-resources"]) then
     local label = p._translate(frame, "Expected resources")
     local label = p._translate(frame, "Expected resources")
     ret[#ret+1] = string.format(
     ret[#ret+1] = string.format(
Line 93: Line 96:
   ret[#ret+1] = p._row_unit  (frame, "Construction area",    args["construction-area"],    "tiles")
   ret[#ret+1] = p._row_unit  (frame, "Construction area",    args["construction-area"],    "tiles")
   ret[#ret+1] = p._row_simple(frame, "Pollution",            args["pollution"])
   ret[#ret+1] = p._row_simple(frame, "Pollution",            args["pollution"])
   ret[#ret+1] = p._row_unit  (frame, sa .. "Spoil time",     args["spoil-time"],            "minutes")
   ret[#ret+1] = p._row_unit  (frame, "Spoil time",           args["spoil-time"],            "minutes", sa)
   ret[#ret+1] = p._row_simple(frame, "Weight",                args["weight"])
   ret[#ret+1] = p._row_simple(frame, "Weight",                args["weight"])
   ret[#ret+1] = p._row_unit  (frame, "Module slots",          args["modules"],              "slots")
   ret[#ret+1] = p._row_unit  (frame, "Module slots",          args["modules"],              "slots")


   if not p._empty_arg(args["prototype-type"]) then
   if not util._empty_arg(args["prototype-type"]) then
     local prototype_type = frame:expandTemplate{title = 'Prototype page', args = {args["prototype-type"]}}
     local prototype_type = frame:expandTemplate{title = 'Prototype page', args = {args["prototype-type"]}}
     ret[#ret+1] = p._row(frame, {"Prototype type", args["prototype-type"], prototype_type})
     ret[#ret+1] = p._row(frame, {"Prototype type", args["prototype-type"], prototype_type})
Line 112: Line 115:
   ret[#ret+1] = p._vrow_item (frame, "Valid fuel",            args["valid-fuel"])
   ret[#ret+1] = p._vrow_item (frame, "Valid fuel",            args["valid-fuel"])
   ret[#ret+1] = p._vrow_item (frame, "Used as fuel by",      args["used-as-fuel-by"])
   ret[#ret+1] = p._vrow_item (frame, "Used as fuel by",      args["used-as-fuel-by"])
   ret[#ret+1] = p._vrow_item (frame, sa .. "Recyling results",args["recycling-results"])
   ret[#ret+1] = p._vrow_item (frame, "Recyling results",     args["recycling-results"], sa)
   ret[#ret+1] = p._extra    (frame,                          args["extra2"])
   ret[#ret+1] = p._extra    (frame,                          args["extra2"])


Line 123: Line 126:
   local sa = frame:expandTemplate{title = "Space age"} .. " "
   local sa = frame:expandTemplate{title = "Space age"} .. " "
    
    
   local recipe = p._arg_or(args["space-age-recipe"], args["recipe"])
   local recipe = util._arg_or(args["space-age-recipe"], args["recipe"])
   if not p._empty_arg(recipe) then
   if not util._empty_arg(recipe) then
     ret[#ret+1] = p._vrow(frame, {"Recipe",    recipe, p._crafting_parsing(frame, recipe)})
     ret[#ret+1] = p._vrow(frame, {"Recipe",    recipe, parsing._crafting(frame, recipe)})
     local vanilla_total_raw = p._arg_or(args["total-raw"], args["recipe"])
     local vanilla_total_raw = util._arg_or(args["total-raw"], args["recipe"])
     ret[#ret+1] = p._vrow(frame, {"Total raw", recipe, p._crafting_raw(frame, p._arg_or(args["space-age-total-raw"],  p._arg_or(args["space-age-recipe"], vanilla_total_raw)))})
     ret[#ret+1] = p._vrow(frame, {"Total raw", recipe, parsing._crafting_raw(frame, util._arg_or(args["space-age-total-raw"],  util._arg_or(args["space-age-recipe"], vanilla_total_raw)))})
   end
   end


   local cost_arg = p._arg_or(args["space-age-cost"], args["cost"])
   local cost_arg = util._arg_or(args["space-age-cost"], args["cost"])
   if not p._empty_arg(cost_arg) then
   if not util._empty_arg(cost_arg) then
     local cost_multiplier_arg = p._arg_or(args["space-age-cost-multiplier"], args["cost-multiplier"])
     local cost_multiplier_arg = util._arg_or(args["space-age-cost-multiplier"], args["cost-multiplier"])
     local cost = p._item_parsing(frame, cost_arg) .. ((not p._empty_arg(cost_multiplier_arg)) and "✖ <big>" .. cost_multiplier_arg .. "</big>" or "")
     local cost = parsing._item(frame, cost_arg) .. ((not util._empty_arg(cost_multiplier_arg)) and "✖ <big>" .. cost_multiplier_arg .. "</big>" or "")
     ret[#ret+1] = p._vrow(frame, {"Cost", cost_arg, cost})
     ret[#ret+1] = p._vrow(frame, {"Cost", cost_arg, cost})
   end
   end
Line 139: Line 142:
   ret[#ret+1] = p._extra    (frame,                          args["extra1"])
   ret[#ret+1] = p._extra    (frame,                          args["extra1"])
    
    
   if not p._empty_arg(args["map-color"]) then
   if not util._empty_arg(args["map-color"]) then
     local color = frame:expandTemplate{title = 'Color', args = {args["map-color"]}}
     local color = frame:expandTemplate{title = 'Color', args = {args["map-color"]}}
     ret[#ret+1] = p._row(frame, {"Map color", args["map-color"], color})
     ret[#ret+1] = p._row(frame, {"Map color", args["map-color"], color})
Line 150: Line 153:
   ret[#ret+1] = p._row_simple(frame, "Fluid storage volume",  args["fluid-storage-volume"])
   ret[#ret+1] = p._row_simple(frame, "Fluid storage volume",  args["fluid-storage-volume"])


   if not p._empty_arg(args["expected-resources"]) then
   if not util._empty_arg(args["expected-resources"]) then
     local label = p._translate(frame, "Expected resources")
     local label = p._translate(frame, "Expected resources")
     ret[#ret+1] = string.format(
     ret[#ret+1] = string.format(
Line 214: Line 217:
   ret[#ret+1] = p._row_unit  (frame, "Construction area",    args["construction-area"],    "tiles")
   ret[#ret+1] = p._row_unit  (frame, "Construction area",    args["construction-area"],    "tiles")
   ret[#ret+1] = p._row_simple(frame, "Pollution",            args["pollution"])
   ret[#ret+1] = p._row_simple(frame, "Pollution",            args["pollution"])
   ret[#ret+1] = p._row_unit  (frame, sa .. "Spoil time",     args["spoil-time"],            "minutes")
   ret[#ret+1] = p._row_unit  (frame, "Spoil time",           args["spoil-time"],            "minutes", sa)
   ret[#ret+1] = p._row_simple(frame, "Weight",                args["weight"])
   ret[#ret+1] = p._row_simple(frame, "Weight",                args["weight"])
   ret[#ret+1] = p._row_unit  (frame, "Module slots",          args["modules"],              "slots")
   ret[#ret+1] = p._row_unit  (frame, "Module slots",          args["modules"],              "slots")


   if not p._empty_arg(args["prototype-type"]) then
   if not util._empty_arg(args["prototype-type"]) then
     local prototype_type = frame:expandTemplate{title = 'Prototype page', args = {args["prototype-type"]}}
     local prototype_type = frame:expandTemplate{title = 'Prototype page', args = {args["prototype-type"]}}
     ret[#ret+1] = p._row(frame, {"Prototype type", args["prototype-type"], prototype_type})
     ret[#ret+1] = p._row(frame, {"Prototype type", args["prototype-type"], prototype_type})
Line 225: Line 228:


   ret[#ret+1] = p._vrow_item (frame, "Accepted equipment",    args["equipment"])
   ret[#ret+1] = p._vrow_item (frame, "Accepted equipment",    args["equipment"])
   ret[#ret+1] = p._vrow_tech (frame, "Required technologies", p._arg_or(args["space-age-required-technologies"], args["required-technologies"]))
   ret[#ret+1] = p._vrow_tech (frame, "Required technologies", util._arg_or(args["space-age-required-technologies"], args["required-technologies"]))
   ret[#ret+1] = p._vrow_tech (frame, "Allows",                p._arg_or(args["space-age-allows"], args["allows"]))
   ret[#ret+1] = p._vrow_tech (frame, "Allows",                util._arg_or(args["space-age-allows"], args["allows"]))
   ret[#ret+1] = p._vrow_item (frame, "Effects",              p._arg_or(args["space-age-effects"], args["effects"]))
   ret[#ret+1] = p._vrow_item (frame, "Effects",              util._arg_or(args["space-age-effects"], args["effects"]))
   ret[#ret+1] = p._vrow_tech (frame, "Boosting technologies", args["boosting-technologies"])
   ret[#ret+1] = p._vrow_tech (frame, "Boosting technologies", args["boosting-technologies"])
   ret[#ret+1] = p._vrow_item (frame, "Produced by",          args["producers"])
   ret[#ret+1] = p._vrow_item (frame, "Produced by",          args["producers"])
   ret[#ret+1] = p._vrow_item (frame, "Consumed by",          p._arg_or(args["space-age-consumers"], args["consumers"]))
   ret[#ret+1] = p._vrow_item (frame, "Consumed by",          util._arg_or(args["space-age-consumers"], args["consumers"]))
   ret[#ret+1] = p._vrow_item (frame, "Valid fuel",            args["valid-fuel"])
   ret[#ret+1] = p._vrow_item (frame, "Valid fuel",            args["valid-fuel"])
   ret[#ret+1] = p._vrow_item (frame, "Used as fuel by",      args["used-as-fuel-by"])
   ret[#ret+1] = p._vrow_item (frame, "Used as fuel by",      args["used-as-fuel-by"])
   ret[#ret+1] = p._vrow_item (frame, sa .. "Recyling results",args["recycling-results"])
   ret[#ret+1] = p._vrow_item (frame, "Recyling results",     args["recycling-results"], sa)
   ret[#ret+1] = p._extra    (frame,                          args["extra2"])
   ret[#ret+1] = p._extra    (frame,                          args["extra2"])


Line 243: Line 246:
end
end


function p._row_simple(frame, label, arg)
function p._row_simple(frame, label, arg, label_prefix)
   if p._empty_arg(arg) then return end
   if util._empty_arg(arg) then return end
    
    
   return p._row(frame, {label, arg})
   return p._row(frame, {label, arg}, label_prefix)
end
end


function p._row_unit(frame, label, arg, unit)
function p._row_unit(frame, label, arg, unit, label_prefix)
   if p._empty_arg(arg) then return end
   if util._empty_arg(arg) then return end
    
    
   return p._row(frame, {label, arg, arg .. " " .. p._translate(frame, unit)})
   return p._row(frame, {label, arg, arg .. " " .. p._translate(frame, unit)}, label_prefix)
end
end


function p._row_type(frame, label, arg)
function p._row_type(frame, label, arg, label_prefix)
   if p._empty_arg(arg) then return end
   if util._empty_arg(arg) then return end
    
    
   return p._row(frame, {label, arg, frame:expandTemplate{title = "Type", args = {arg}}})
   return p._row(frame, {label, arg, frame:expandTemplate{title = "Type", args = {arg}}}, label_prefix)
end
end


function p._row_item(frame, label, arg)
function p._row_item(frame, label, arg, label_prefix)
   if p._empty_arg(arg) then return end
   if util._empty_arg(arg) then return end
    
    
   return p._row(frame, {label, arg, p._item_parsing(frame, arg)})
   return p._row(frame, {label, arg, parsing._item(frame, arg)}, label_prefix)
end
end


function p._vrow_item(frame, label, arg)
function p._vrow_item(frame, label, arg, label_prefix)
   if p._empty_arg(arg) then return end
   if util._empty_arg(arg) or arg == "none" then return end
    
    
   return p._vrow(frame, {label, arg, p._item_parsing(frame, arg)})
   return p._vrow(frame, {label, arg, parsing._item(frame, arg)}, label_prefix)
end
end


function p._vrow_tech(frame, label, arg)
function p._vrow_tech(frame, label, arg, label_prefix)
   if p._empty_arg(arg) then return end
   if util._empty_arg(arg) or arg == "none" then return end
    
    
   return p._vrow(frame, {label, arg, p._technology_parsing(frame, {arg, color = "228B22"})})
   return p._vrow(frame, {label, arg, parsing._technology(frame, {arg, color = "228B22"})}, label_prefix)
end
end


function p._row(frame, args)
function p._row(frame, args, label_prefix)
   if p._empty_arg(args[2]) then
   if util._empty_arg(args[2]) then
     return
     return
   end
   end


   local label = p._translate(frame, args[1])
   local label = p._translate(frame, args[1])
  if label_prefix then
    label = label_prefix .. label
  end


   return string.format(
   return string.format(
Line 299: Line 305:
end
end


function p._vrow(frame, args)
function p._vrow(frame, args, label_prefix)
   if p._empty_arg(args[2]) then
   if util._empty_arg(args[2]) then
     return
     return
   end
   end


   local label = p._translate(frame, args[1])
   local label = p._translate(frame, args[1])
  if label_prefix then
    label = label_prefix .. label
  end


   return string.format(
   return string.format(
Line 322: Line 331:


function p._extra(frame, arg)
function p._extra(frame, arg)
   if p._empty_arg(arg) then
   if util._empty_arg(arg) then
     return
     return
   end
   end
Line 335: Line 344:
</tr>]],
</tr>]],
     label)
     label)
end
function p.technology_parsing(frame)
  return p._technology_parsing(frame, frame.args)
end
function p._technology_parsing(frame, args)
  if args[1] == "?" or p._empty_arg(args[1]) then
    return args[1]
  end
  local ret = {}
  local techs = p._split(args[1], "+")
  for _, tech in ipairs(techs) do
    local tech_parts = p._split(tech, ",")
    local tech_name = tech_parts[1] .. " (research)"
    if not p._page_exists(tech_name) then -- fall back to plain name if page with " (research)" doesn't exist
      tech_name = tech_parts[1]
    end
    local tech_level = tech_parts[2] or ""
    if tech_level == "" then -- fall back to level from tech name if none given manually
      local second_to_last_char = string.sub(tech_parts[1], -2, -2)
      if second_to_last_char == " " then
        tech_level = string.sub(tech_parts[1], -1) -- last character should be the level of the tech
      end
    end
    ret[#ret+1] = frame:expandTemplate{title = 'icon/special', args = {tech_name, tech_level, color=args.color or "999"}}
  end
  return table.concat(ret)
end
function p.item_parsing(frame)
  return p._item_parsing(frame, frame.args[1])
end
function p._item_parsing(frame, item_string)
  if item_string == "?" or p._empty_arg(item_string) then
    return item_string
  end
  local ret = {}
  local items = p._split(item_string, "+")
  for _, item in ipairs(items) do
    local icon_args = p._split(item, ",")
    icon_args[2] = icon_args[2] or ""
    ret[#ret+1] = frame:expandTemplate{title = 'icon/special', args = icon_args}
  end
  return table.concat(ret)
end
function p.crafting_parsing(frame)
  return p._crafting_parsing(frame, frame.args[1])
end
function p._crafting_parsing(frame, recipe)
  if recipe == "?" or p._empty_arg(recipe) then
    return recipe
  end
 
  local recipe_parts = p._split(recipe, "=") 
  local ingredients = p._crafting_parsing_split(frame, recipe_parts[1])
 
  local products = recipe_parts[2]
  if products then
    products = "&rarr; " .. p._crafting_parsing_split(frame, products)
  else
    local item_name = frame:expandTemplate{title = 'No language suffix/No namespace'}
    products = "&rarr; " .. frame:expandTemplate{title = 'icon/special', args = {item_name, "1"}}
  end
 
  return ingredients .. products
end
function p.crafting_raw(frame)
  return p._crafting_raw(frame, frame.args[1])
end
function p._crafting_raw(frame, recipe)
  if recipe == "?" or p._empty_arg(recipe) then
    return recipe
  end
  local recipe_parts = p._split(recipe, "=")
  return p._crafting_parsing_split(frame, recipe_parts[1])
end
-- @param frame
-- @param str string A list of "item, count" separated by "+"
-- @return string @The resulting icons with a "+" between each icon
function p._crafting_parsing_split(frame, str)
  local ret = {}
  local items = p._split(str, "+")
  for _, item in ipairs(items) do
    local item_parts = p._split(item, ",")
    local item_count = item_parts[2] or "1" -- fall back to 1, not empty!
    item_count = frame:expandTemplate{title = 'Crop', args = {item_count}}
    ret[#ret+1] = frame:expandTemplate{title = 'icon/special', args = {item_parts[1], item_count}}
  end
  return table.concat(ret, "+")
end
function p._arg_or(arg, default)
  if p._empty_arg(arg) then
    return default
  else
    return arg
  end
end
-- @param arg string The argument to check
-- @return boolean @Whether the argument is an whitespace string or empty string or nil
function p._empty_arg(arg)
  if type(arg) == "string" then
    return not string.find(arg, "%S")
  else
    return not arg
  end
end
-- @param inputstr string A string to be split
-- @param separator string The separator
-- @return string[] @The separated strings, without the separator
function p._split(inputstr, separator)
  local result = {}
  for str in string.gmatch(inputstr, "([^"..separator.."]+)") do
    table.insert(result, mw.text.trim(str))
  end
  return result
end
-- @param page_title string The title of the page
-- @return boolean
function p._page_exists(page_title)
  if page_title and page_title ~= "" then
    return mw.title.new(page_title).exists
  else
    return false
  end
end
end


return p
return p

Revision as of 12:38, 25 October 2024

Lua methods used by Template:Infobox. Uses Lua methods from Module:Infobox/parsing and Module:Util.

Edit this documentation on Module:Infobox/doc.

Methods

base_tab

space_age_tab


local util = require("Module:Util")
local parsing = require("Module:Infobox/parsing")

local p = {}

function p.base_tab(frame) -- no prefix for args. Used for vanilla tab or if no tabs
  local args = frame:getParent().args
  local ret = {}
  local sa = frame:expandTemplate{title = "Space age"} .. " "

  if not util._empty_arg(args["recipe"]) then
    ret[#ret+1] = p._vrow(frame, {"Recipe",    args["recipe"], parsing._crafting(frame, args["recipe"])})
    ret[#ret+1] = p._vrow(frame, {"Total raw", args["recipe"], parsing._crafting_raw(frame, util._arg_or(args["total-raw"], args["recipe"]))})
  end

  if not util._empty_arg(args["cost"]) then
    local cost = parsing._item(frame, args["cost"]) .. ((not util._empty_arg(args["cost-multiplier"])) and "✖ <big>" .. args["cost-multiplier"] .. "</big>" or "")
    ret[#ret+1] = p._vrow(frame, {"Cost", args["cost"], cost})
  end

  ret[#ret+1] = p._extra     (frame,                          args["extra1"])
  
  if not util._empty_arg(args["map-color"]) then
    local color = frame:expandTemplate{title = 'Color', args = {args["map-color"]}}
    ret[#ret+1] = p._row(frame, {"Map color", args["map-color"], color})
  end

  ret[#ret+1] = p._row_simple(frame, "Map icon",              args["map-icon"])
  ret[#ret+1] = p._row_simple(frame, "Added in",              args["added-in"])
  ret[#ret+1] = p._row_simple(frame, "Walking speed",         args["walking-speed"])
  ret[#ret+1] = p._row_simple(frame, "Storage size",          args["storage-size"])
  ret[#ret+1] = p._row_simple(frame, "Fluid storage volume",  args["fluid-storage-volume"])

  if not util._empty_arg(args["expected-resources"]) then
    local label = p._translate(frame, "Expected resources")
    ret[#ret+1] = string.format(
[[<tr class="border-top">
<td>
%s
</td>
<td style="width: 70%%;">
%s
</td>
</tr>]], label, args["expected-resources"])
  end

  ret[#ret+1] = p._row_simple(frame, "Health",                args["health"])
  ret[#ret+1] = p._row_unit  (frame, "Restores",              args["restores"],             "health")
  ret[#ret+1] = p._row_simple(frame, "Resistances",           args["resistance"])
  ret[#ret+1] = p._row_unit  (frame, "Lifespan",              args["lifespan"],             "seconds")
  ret[#ret+1] = p._row_simple(frame, "Inventory size bonus",  args["inventory-size-bonus"])
  ret[#ret+1] = p._row_simple(frame, "Equipment grid size",   args["grid-size"])
  ret[#ret+1] = p._row_simple(frame, "Stack size",            args["stack-size"])
  ret[#ret+1] = p._row_simple(frame, "Range",                 args["range"])
  ret[#ret+1] = p._row_simple(frame, "Shooting speed",        args["shooting-speed"])
  ret[#ret+1] = p._row_simple(frame, "Damage",                args["damage"])
  ret[#ret+1] = p._row_simple(frame, "Damage bonus",          args["damage-bonus"])
  ret[#ret+1] = p._row_simple(frame, "Cluster size",          args["cluster-size"])
  ret[#ret+1] = p._row_simple(frame, "Area of effect size",   args["area-of-effect-size"])
  ret[#ret+1] = p._row_simple(frame, "Durability",            args["durability"])
  ret[#ret+1] = p._row_simple(frame, "Magazine size",         args["magazine-size"])
  ret[#ret+1] = p._row_item  (frame, "Ammunition",            args["ammunition"])
  ret[#ret+1] = p._vrow_item (frame, "Used as ammunition by", args["used-as-ammo-by"])
  ret[#ret+1] = p._row_simple(frame, "Efficiency",            args["efficiency"])
  ret[#ret+1] = p._row_simple(frame, "Dimensions",            args["dimensions"])
  ret[#ret+1] = p._row_type  (frame, "Energy consumption",    args["energy"])
  ret[#ret+1] = p._row_type  (frame, "Drain",                 args["drain"])
  ret[#ret+1] = p._row_type  (frame, "Robot recharge rate",   args["robot-recharge-rate"])
  ret[#ret+1] = p._row_type  (frame, "Internal buffer recharge rate", args["internal-buffer-recharge-rate"])
  ret[#ret+1] = p._vrow_item (frame, "Placed in",             args["equipped-in"])
  ret[#ret+1] = p._row_simple(frame, "Robot limit",           args["robot-limit"])
  ret[#ret+1] = p._row_simple(frame, "Repair speed",          args["repair-speed"])
  ret[#ret+1] = p._row_simple(frame, "Charging stations",     args["charging-stations"])
  ret[#ret+1] = p._row_simple(frame, "Belt speed",            args["belt-speed"])
  ret[#ret+1] = p._row_simple(frame, "Movement bonus",        args["movement-bonus"])
  ret[#ret+1] = p._row_type  (frame, "Energy capacity",       args["energy-capacity"])
  ret[#ret+1] = p._row_simple(frame, "Power input",           args["power-input"])
  ret[#ret+1] = p._row_simple(frame, "Power output",          args["power-output"])
  ret[#ret+1] = p._row_simple(frame, "Maximum temperature",   args["maximum-temperature"])
  ret[#ret+1] = p._row_simple(frame, "Fluid consumption",     args["fluid-consumption"])
  ret[#ret+1] = p._row_simple(frame, "Shield hitpoints",      args["shield"])
  ret[#ret+1] = p._row_type  (frame, "Energy per hitpoint",   args["energy-per-hitpoint"])
  ret[#ret+1] = p._row_simple(frame, "Maximum recharge speed",args["maximum-recharge-speed"])
  ret[#ret+1] = p._row_simple(frame, "Crafting speed",        args["crafting-speed"])
  ret[#ret+1] = p._row_simple(frame, "Pumping speed",         args["pumping-speed"])
  ret[#ret+1] = p._row_simple(frame, "Mining time",           args["mining-time"])
  ret[#ret+1] = p._row_simple(frame, "Speed",                 args["speed"])
  ret[#ret+1] = p._row_simple(frame, "Productivity",          args["productivity"])
  ret[#ret+1] = p._row_simple(frame, "Mining speed",          args["mining-speed"])
  ret[#ret+1] = p._row_unit  (frame, "Mining area",           args["mining-area"],           "tiles")
  ret[#ret+1] = p._row_type  (frame, "Fuel value",            args["fuel-value"])
  ret[#ret+1] = p._row_simple(frame, "Vehicle acceleration",  args["vehicle-acceleration"])
  ret[#ret+1] = p._row_simple(frame, "Vehicle top speed",     args["vehicle-top-speed"])
  ret[#ret+1] = p._row_unit  (frame, "Supply area",           args["supply-area"],           "tiles")
  ret[#ret+1] = p._row_unit  (frame, "Wire reach",            args["wire-reach"],            "tiles")
  ret[#ret+1] = p._row_unit  (frame, "Construction area",     args["construction-area"],     "tiles")
  ret[#ret+1] = p._row_simple(frame, "Pollution",             args["pollution"])
  ret[#ret+1] = p._row_unit  (frame, "Spoil time",            args["spoil-time"],            "minutes", sa)
  ret[#ret+1] = p._row_simple(frame, "Weight",                args["weight"])
  ret[#ret+1] = p._row_unit  (frame, "Module slots",          args["modules"],               "slots")

  if not util._empty_arg(args["prototype-type"]) then
    local prototype_type = frame:expandTemplate{title = 'Prototype page', args = {args["prototype-type"]}}
    ret[#ret+1] = p._row(frame, {"Prototype type", args["prototype-type"], prototype_type})
  end
  ret[#ret+1] = p._row_simple(frame, "Internal name",         args["internal-name"])

  ret[#ret+1] = p._vrow_item (frame, "Accepted equipment",    args["equipment"])
  ret[#ret+1] = p._vrow_tech (frame, "Required technologies", args["required-technologies"])
  ret[#ret+1] = p._vrow_tech (frame, "Allows",                args["allows"])
  ret[#ret+1] = p._vrow_item (frame, "Effects",               args["effects"])
  ret[#ret+1] = p._vrow_tech (frame, "Boosting technologies", args["boosting-technologies"])
  ret[#ret+1] = p._vrow_item (frame, "Produced by",           args["producers"])
  ret[#ret+1] = p._vrow_item (frame, "Consumed by",           args["consumers"])
  ret[#ret+1] = p._vrow_item (frame, "Valid fuel",            args["valid-fuel"])
  ret[#ret+1] = p._vrow_item (frame, "Used as fuel by",       args["used-as-fuel-by"])
  ret[#ret+1] = p._vrow_item (frame, "Recyling results",      args["recycling-results"], sa)
  ret[#ret+1] = p._extra     (frame,                          args["extra2"])

  return table.concat(ret)
end

function p.space_age_tab(frame) -- space-age prefix for some args. Used for space age mod tab
  local args = frame:getParent().args
  local ret = {}
  local sa = frame:expandTemplate{title = "Space age"} .. " "
  
  local recipe = util._arg_or(args["space-age-recipe"], args["recipe"])
  if not util._empty_arg(recipe) then
    ret[#ret+1] = p._vrow(frame, {"Recipe",    recipe, parsing._crafting(frame, recipe)})
    local vanilla_total_raw = util._arg_or(args["total-raw"], args["recipe"])
    ret[#ret+1] = p._vrow(frame, {"Total raw", recipe, parsing._crafting_raw(frame, util._arg_or(args["space-age-total-raw"],  util._arg_or(args["space-age-recipe"], vanilla_total_raw)))})
  end

  local cost_arg = util._arg_or(args["space-age-cost"], args["cost"])
  if not util._empty_arg(cost_arg) then
    local cost_multiplier_arg = util._arg_or(args["space-age-cost-multiplier"], args["cost-multiplier"])
    local cost = parsing._item(frame, cost_arg) .. ((not util._empty_arg(cost_multiplier_arg)) and "✖ <big>" .. cost_multiplier_arg .. "</big>" or "")
    ret[#ret+1] = p._vrow(frame, {"Cost", cost_arg, cost})
  end

  ret[#ret+1] = p._extra     (frame,                          args["extra1"])
  
  if not util._empty_arg(args["map-color"]) then
    local color = frame:expandTemplate{title = 'Color', args = {args["map-color"]}}
    ret[#ret+1] = p._row(frame, {"Map color", args["map-color"], color})
  end

  ret[#ret+1] = p._row_simple(frame, "Map icon",              args["map-icon"])
  ret[#ret+1] = p._row_simple(frame, "Added in",              args["added-in"])
  ret[#ret+1] = p._row_simple(frame, "Walking speed",         args["walking-speed"])
  ret[#ret+1] = p._row_simple(frame, "Storage size",          args["storage-size"])
  ret[#ret+1] = p._row_simple(frame, "Fluid storage volume",  args["fluid-storage-volume"])

  if not util._empty_arg(args["expected-resources"]) then
    local label = p._translate(frame, "Expected resources")
    ret[#ret+1] = string.format(
[[<tr class="border-top">
<td>
%s
</td>
<td style="width: 70%%;">
%s
</td>
</tr>]], label, args["expected-resources"])
  end

  ret[#ret+1] = p._row_simple(frame, "Health",                args["health"])
  ret[#ret+1] = p._row_unit  (frame, "Restores",              args["restores"],             "health")
  ret[#ret+1] = p._row_simple(frame, "Resistances",           args["resistance"])
  ret[#ret+1] = p._row_unit  (frame, "Lifespan",              args["lifespan"],             "seconds")
  ret[#ret+1] = p._row_simple(frame, "Inventory size bonus",  args["inventory-size-bonus"])
  ret[#ret+1] = p._row_simple(frame, "Equipment grid size",   args["grid-size"])
  ret[#ret+1] = p._row_simple(frame, "Stack size",            args["stack-size"])
  ret[#ret+1] = p._row_simple(frame, "Range",                 args["range"])
  ret[#ret+1] = p._row_simple(frame, "Shooting speed",        args["shooting-speed"])
  ret[#ret+1] = p._row_simple(frame, "Damage",                args["damage"])
  ret[#ret+1] = p._row_simple(frame, "Damage bonus",          args["damage-bonus"])
  ret[#ret+1] = p._row_simple(frame, "Cluster size",          args["cluster-size"])
  ret[#ret+1] = p._row_simple(frame, "Area of effect size",   args["area-of-effect-size"])
  ret[#ret+1] = p._row_simple(frame, "Durability",            args["durability"])
  ret[#ret+1] = p._row_simple(frame, "Magazine size",         args["magazine-size"])
  ret[#ret+1] = p._row_item  (frame, "Ammunition",            args["ammunition"])
  ret[#ret+1] = p._vrow_item (frame, "Used as ammunition by", args["used-as-ammo-by"])
  ret[#ret+1] = p._row_simple(frame, "Efficiency",            args["efficiency"])
  ret[#ret+1] = p._row_simple(frame, "Dimensions",            args["dimensions"])
  ret[#ret+1] = p._row_type  (frame, "Energy consumption",    args["energy"])
  ret[#ret+1] = p._row_type  (frame, "Drain",                 args["drain"])
  ret[#ret+1] = p._row_type  (frame, "Robot recharge rate",   args["robot-recharge-rate"])
  ret[#ret+1] = p._row_type  (frame, "Internal buffer recharge rate", args["internal-buffer-recharge-rate"])
  ret[#ret+1] = p._vrow_item (frame, "Placed in",             args["equipped-in"])
  ret[#ret+1] = p._row_simple(frame, "Robot limit",           args["robot-limit"])
  ret[#ret+1] = p._row_simple(frame, "Repair speed",          args["repair-speed"])
  ret[#ret+1] = p._row_simple(frame, "Charging stations",     args["charging-stations"])
  ret[#ret+1] = p._row_simple(frame, "Belt speed",            args["belt-speed"])
  ret[#ret+1] = p._row_simple(frame, "Movement bonus",        args["movement-bonus"])
  ret[#ret+1] = p._row_type  (frame, "Energy capacity",       args["energy-capacity"])
  ret[#ret+1] = p._row_simple(frame, "Power input",           args["power-input"])
  ret[#ret+1] = p._row_simple(frame, "Power output",          args["power-output"])
  ret[#ret+1] = p._row_simple(frame, "Maximum temperature",   args["maximum-temperature"])
  ret[#ret+1] = p._row_simple(frame, "Fluid consumption",     args["fluid-consumption"])
  ret[#ret+1] = p._row_simple(frame, "Shield hitpoints",      args["shield"])
  ret[#ret+1] = p._row_type  (frame, "Energy per hitpoint",   args["energy-per-hitpoint"])
  ret[#ret+1] = p._row_simple(frame, "Maximum recharge speed",args["maximum-recharge-speed"])
  ret[#ret+1] = p._row_simple(frame, "Crafting speed",        args["crafting-speed"])
  ret[#ret+1] = p._row_simple(frame, "Pumping speed",         args["pumping-speed"])
  ret[#ret+1] = p._row_simple(frame, "Mining time",           args["mining-time"])
  ret[#ret+1] = p._row_simple(frame, "Speed",                 args["speed"])
  ret[#ret+1] = p._row_simple(frame, "Productivity",          args["productivity"])
  ret[#ret+1] = p._row_simple(frame, "Mining speed",          args["mining-speed"])
  ret[#ret+1] = p._row_unit  (frame, "Mining area",           args["mining-area"],           "tiles")
  ret[#ret+1] = p._row_type  (frame, "Fuel value",            args["fuel-value"])
  ret[#ret+1] = p._row_simple(frame, "Vehicle acceleration",  args["vehicle-acceleration"])
  ret[#ret+1] = p._row_simple(frame, "Vehicle top speed",     args["vehicle-top-speed"])
  ret[#ret+1] = p._row_unit  (frame, "Supply area",           args["supply-area"],           "tiles")
  ret[#ret+1] = p._row_unit  (frame, "Wire reach",            args["wire-reach"],            "tiles")
  ret[#ret+1] = p._row_unit  (frame, "Construction area",     args["construction-area"],     "tiles")
  ret[#ret+1] = p._row_simple(frame, "Pollution",             args["pollution"])
  ret[#ret+1] = p._row_unit  (frame, "Spoil time",            args["spoil-time"],            "minutes", sa)
  ret[#ret+1] = p._row_simple(frame, "Weight",                args["weight"])
  ret[#ret+1] = p._row_unit  (frame, "Module slots",          args["modules"],               "slots")

  if not util._empty_arg(args["prototype-type"]) then
    local prototype_type = frame:expandTemplate{title = 'Prototype page', args = {args["prototype-type"]}}
    ret[#ret+1] = p._row(frame, {"Prototype type", args["prototype-type"], prototype_type})
  end
  ret[#ret+1] = p._row_simple(frame, "Internal name",         args["internal-name"])

  ret[#ret+1] = p._vrow_item (frame, "Accepted equipment",    args["equipment"])
  ret[#ret+1] = p._vrow_tech (frame, "Required technologies", util._arg_or(args["space-age-required-technologies"], args["required-technologies"]))
  ret[#ret+1] = p._vrow_tech (frame, "Allows",                util._arg_or(args["space-age-allows"], args["allows"]))
  ret[#ret+1] = p._vrow_item (frame, "Effects",               util._arg_or(args["space-age-effects"], args["effects"]))
  ret[#ret+1] = p._vrow_tech (frame, "Boosting technologies", args["boosting-technologies"])
  ret[#ret+1] = p._vrow_item (frame, "Produced by",           args["producers"])
  ret[#ret+1] = p._vrow_item (frame, "Consumed by",           util._arg_or(args["space-age-consumers"], args["consumers"]))
  ret[#ret+1] = p._vrow_item (frame, "Valid fuel",            args["valid-fuel"])
  ret[#ret+1] = p._vrow_item (frame, "Used as fuel by",       args["used-as-fuel-by"])
  ret[#ret+1] = p._vrow_item (frame, "Recyling results",      args["recycling-results"], sa)
  ret[#ret+1] = p._extra     (frame,                          args["extra2"])

  return table.concat(ret)
end

function p._translate(frame, str)
  return frame:expandTemplate{title = "Translation", args = {str}}
end

function p._row_simple(frame, label, arg, label_prefix)
  if util._empty_arg(arg) then return end
  
  return p._row(frame, {label, arg}, label_prefix)
end

function p._row_unit(frame, label, arg, unit, label_prefix)
  if util._empty_arg(arg) then return end
  
  return p._row(frame, {label, arg, arg .. " " .. p._translate(frame, unit)}, label_prefix)
end

function p._row_type(frame, label, arg, label_prefix)
  if util._empty_arg(arg) then return end
  
  return p._row(frame, {label, arg, frame:expandTemplate{title = "Type", args = {arg}}}, label_prefix)
end

function p._row_item(frame, label, arg, label_prefix)
  if util._empty_arg(arg) then return end
  
  return p._row(frame, {label, arg, parsing._item(frame, arg)}, label_prefix)
end

function p._vrow_item(frame, label, arg, label_prefix)
  if util._empty_arg(arg) or arg == "none" then return end
  
  return p._vrow(frame, {label, arg, parsing._item(frame, arg)}, label_prefix)
end

function p._vrow_tech(frame, label, arg, label_prefix)
  if util._empty_arg(arg) or arg == "none" then return end
  
  return p._vrow(frame, {label, arg, parsing._technology(frame, {arg, color = "228B22"})}, label_prefix)
end

function p._row(frame, args, label_prefix)
  if util._empty_arg(args[2]) then
    return
  end

  local label = p._translate(frame, args[1])
  if label_prefix then
    label = label_prefix .. label
  end

  return string.format(
[[<tr class="border-top">
<td>
%s
</td>
<td>
%s
</td>
</tr>]],
    label,
    args[3] or args[2])
end

function p._vrow(frame, args, label_prefix)
  if util._empty_arg(args[2]) then
    return
  end

  local label = p._translate(frame, args[1])
  if label_prefix then
    label = label_prefix .. label
  end

  return string.format(
[[<tr class="border-top">
<td colspan=2>
%s
</td>
</tr>
<tr>
<td class="infobox-vrow-value" colspan=2>
%s
</td>
</tr>]],
    label,
    args[3] or args[2])
end

function p._extra(frame, arg)
  if util._empty_arg(arg) then
    return
  end

  local label = p._translate(frame, arg)

  return string.format(
[[<tr class="border-top">
<td colspan=2 class="infobox-extra">
%s
</td>
</tr>]],
    label)
end

return p