Module:Shopbox: Difference between revisions

From Against the Storm Official Wiki
(fixing typos)
(Updated to use new WorkshopsData. No longer stores external data but uses new getter methods. Improved documentation.)
Line 1: Line 1:
---
---
-- Module to create infobox for displaying on a workshop's wiki page.
--- Module to create an infobox for displaying on a workshop's wiki page.
--
---
-- @module Shopbox
--- Shows the facts from the data about the specified workshop in an easy-to-
--- read table, with a large icon, information, categories, and the flavor
--- text.
---
--- This should be invoked from Template:Shopbox with the parameter of the
--- workshop whose infobox should be shown. See the template documentation for
--- more information about parameters and errors and to see examples.
---
--- @module Shopbox
local Shopbox = {}
local Shopbox = {}


---
-- Dependencies
---
local RecipeData = require("Module:RecipeData")


---
 
-- Constants
local WorkshopsData = require("Module:WorkshopsData")
---
local ResourceLink = require("Module:ResourceLink")
 
 
 
--region Private constants
 
-- External templates
local TEMPLATE_RL_ICON = "med"
 
-- Template parameters
local ARG_WORKSHOP_NAME = "name"
local ARG_WORKSHOP_NAME = "name"


local INDEX_WORKSHOP_ID = 1
-- Infobox header labels
local INDEX_WORKSHOP_NAME = 2
local INDEX_WORKSHOP_DESCRIPTION = 3
local INDEX_WORKSHOP_CATEGORY = 4
local INDEX_WORKSHOP_SIZE_X = 5
local INDEX_WORKSHOP_SIZE_Y = 6
local INDEX_WORKSHOP_CITY_SCORE = 7
local INDEX_WORKSHOP_MOVABLE = 8
local INDEX_WORKSHOP_INITIALLY_ESSENTIAL = 9
local INDEX_WORKSHOP_STORAGE = 10
local INDEX_WORKSHOP_CONSTRUCTION_TIME = 11
local INDEX_WORKSHOP_REQUIRED_GOODS = 12
local INDEX_WORKSHOP_WORKPLACES = 13
local INDEX_WORKSHOP_RECIPES = 14
 
local TITLE_ID = "ID"
local TITLE_ID = "ID"
local TITLE_CATEGORY = "Toolbar Category"
local TITLE_CATEGORY = "Toolbar Category"
local TITLE_SIZE = "Building Footprint"
local TITLE_SIZE = "Building Size"
local TITLE_MOVABLE = "Movable"
local TITLE_MOVABLE = "Movable"
local TITLE_ESSENTIAL = "Initially Essential"
local TITLE_ESSENTIAL = "Initially Essential"
Line 41: Line 40:
local TITLE_RECIPES = "Recipes"
local TITLE_RECIPES = "Recipes"


local VALUE_TYPE = "Production Building"
-- Subheading for all the buildings from this template.
local BUILDING_CATEGORY = "Production Building"
 
local INDEX_REQ_STACK_SIZE = "stackSize"
local INDEX_REQ_GOOD_ID = "goodID"


local BOLD = "'''"
local BOLD = "'''"
local NBSP = " "
local NBSP = " "
local WIKIMARKUP_MED_ICON_ALT = "|32px|alt="


local WIKI_MED_ICON_MARKUP = "|32px|alt="
local CSS_INLINE_INFOBOX = {
 
local categoryIcons = {
["Food Production"] = "Construct Food Production.png",
["Industry"] = "Construct Industry.png"
}
 
local CSS_INFOBOX_INLINE_STYLE_TABLE = {  
["float"] = "right",  
["float"] = "right",  
["clear"] = "right",  
["clear"] = "right",  
Line 59: Line 56:
["border"] = "1px solid #a2a9b1",  
["border"] = "1px solid #a2a9b1",  
["border-spacing"] = "3px",
["border-spacing"] = "3px",
["border-collapse"] = "collapse",
["margin"] = "0.5em 0 0.5em 1em"
["margin"] = "0.5em 0 0.5em 1em"
}
local CSS_INLINE_INFOBOX_INNER_TABLE = {
["border-collapse"] = "collapse"
}
local CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER = {
["border-bottom"] = "1px solid #a2a9b1"
}
local CSS_INLINE_INFOBOX_TR_TOP_DIVIDER = {
["border-top"] = "1px solid #a2a9b1"
}
}


-- Only two valid construction toolbar categories for buildings from this
-- template.
local ICON_FILENAMES_CATEGORIES = {
["Food Production"] = "Construct Food Production.png",
["Industry"] = "Construct Industry.png"
}


--endregion


--region Private methods
---
--- A shortcut method that redirects to ResourceLink to build links to goods
--- with their IDs from the tables of required goods.
---
---
-- Member variable: Workshop
---@param goodID string the ID of the good to link to
--  
---@return string wikimarkup representing an icon and link
local workshop
local function resourceLink(goodID)
 
return ResourceLink.resourceLinkWithID(goodID, TEMPLATE_RL_ICON)
end






---
---
-- Creates an html table to display the data in a floating box.
--- Lays out an inner, no-styling table to display construction goods with
--
--- their stack sizes.
-- @param frame the template's calling context
---
-- @return wiki markup for the box
--- @param requiredGoods table of required goods, with stack sizes and IDs
function Shopbox.infobox(frame)
--- @return string wikimarkup for a table of required goods to insert in other markup
local function writeRequiredGoodsRows(requiredGoods)
-- Every workshop must have a name.
 
local name = frame.args[ARG_WORKSHOP_NAME]
if not requiredGoods or #requiredGoods == 0 then
error("Cannot write content with provided table of required goods.")
if not name or name == "" then
end
error("You must specify the name of the building in the Shopbox template.")
 
local requiredGoodsRows = mw.html.create("table")
requiredGoodsRows:newline()
 
for _, requiredGroup in ipairs(requiredGoods) do
 
local stackSize = requiredGroup[INDEX_REQ_STACK_SIZE]
local goodID = requiredGroup[INDEX_REQ_GOOD_ID]
 
requiredGoodsRows:tag("tr")
:tag("td"):wikitext(BOLD .. stackSize .. BOLD):done()
:tag("td"):wikitext(resourceLink(goodID)):done()
:done():newline()
end
end
 
-- Custom parameters
return requiredGoodsRows
end
-- Retrieve the actual data for the workshop.
 
workshop = RecipeData.getAllDataForWorkshop(name)
 
 
if not workshop then
---
return "No building found for infobox."
--- Lays out the provided recipe IDs in a small list.
---
---@param recipes table of recipe IDs
---@return string wiki markup of a list of those IDs
local function writeRecipesRows(recipes)
 
if not recipes or #recipes == 0 then
error("Cannot write content with provided table of recipes.")
end
end
 
local workshopID = workshop[INDEX_WORKSHOP_ID]
local recipeRows = mw.html.create("ul")
local workshopDescription = workshop[INDEX_WORKSHOP_DESCRIPTION]
recipeRows:newline()
local workshopCategory = workshop[INDEX_WORKSHOP_CATEGORY]
 
local workshopSizeX = workshop[INDEX_WORKSHOP_SIZE_X]
for _, recipeID in ipairs(recipes) do
local workshopSizeY = workshop[INDEX_WORKSHOP_SIZE_Y]
-- Temporarily, just write the ID of the recipe.
local workshopIsMovable = workshop[INDEX_WORKSHOP_MOVABLE]
recipeRows:tag("li"):wikitext(recipeID):done():newline()
local workshopIsEssential = workshop[INDEX_WORKSHOP_INITIALLY_ESSENTIAL]
local workshopStorageCap = workshop[INDEX_WORKSHOP_STORAGE]
local workshopConstructionTime = workshop[INDEX_WORKSHOP_CONSTRUCTION_TIME]
local workshopConstructionGoods = workshop[INDEX_WORKSHOP_REQUIRED_GOODS]
local workshopWorkplaces = workshop[INDEX_WORKSHOP_WORKPLACES]
local workshopRecipes = workshop[INDEX_WORKSHOP_RECIPES]
local _, workshopIconFilename = RecipeData.getWorkshopNameAndIcon(workshopID)
-- Make the top of the infobox that every item has.
wikiInfobox = mw.html.create("table"):css( CSS_INFOBOX_INLINE_STYLE_TABLE ):newline()
-- Title of infobox.
if name then
wikiInfobox:tag("tr"):tag("td"):wikitext(name):done():done():newline()
end
end
 
-- Subtitle for category.
recipeRows:done()
wikiInfobox:tag("tr"):tag("td"):wikitext(VALUE_TYPE):done():done():newline()
 
return recipeRows
-- Large icon of infobox.
end
if workshopIconFilename then
 
wikiInfobox:tag("tr"):tag("td"):wikitext("[[File:" .. workshopIconFilename .. ".png]]"):done():done():newline()
 
wikiInfobox:tag("tr"):tag("td"):wikitext(name .. " icon as seen in-game"):done():done():newline()
 
---
--- Builds using the provided wikiInfobox a subtable to lay out headers and
--- fields.
---
---@param wikiInfobox table mw.html object into which we're building this
---@param workshopName string the name of the good the infobox is about
---@return table the Mediawiki html object into which we've been adding things
local function makeInnerTable(wikiInfobox, workshopName)
 
-- Grab the data we'll use to populate this.
local workshopID = WorkshopsData.getWorkshopID(workshopName)
local workshopCategory = WorkshopsData.getWorkshopCategory(workshopName)
local workshopSizeX, workshopSizeY = WorkshopsData.getWorkshopSize(workshopName)
local workshopIsMovable = WorkshopsData.isWorkshopMovable(workshopName)
local workshopIsEssential = WorkshopsData.isWorkshopInitiallyEssential(workshopName)
local workshopStorageCap = WorkshopsData.getWorkshopStorage(workshopName)
local workshopConstructionTime = WorkshopsData.getWorkshopConstructionTime(workshopName)
local workshopRequiredGoods = WorkshopsData.getWorkshopRequiredGoods(workshopName)
local workshopRecipesTable = WorkshopsData.getWorkshopRecipes(workshopName)
local workshopWorkplacesTable = WorkshopsData.getWorkshopWorkplaces(workshopName)
 
-- Only need to count these, just do it right away.
local workshopWorkplaces
if not workshopWorkplacesTable then
workshopWorkplaces = #workshopWorkplacesTable
else
workshopWorkplaces = nil
end
end
 
-- Data fields of the infobox with labels and values
-- Start the inner table
local innerTable = wikiInfobox:tag("tr"):tag("td"):tag("table")
local innerTable = wikiInfobox:tag("tr"):tag("td"):newline():tag("table"):css(CSS_INLINE_INFOBOX_INNER_TABLE)
innerTable:newline()
innerTable:newline()
 
-- Data provided by the CSV
-- Additional parameters would go here.
 
-- Construction toolbar category.
if workshopCategory then
if workshopCategory then
local catIcon = "[[File:" .. categoryIcons[workshopCategory] .. WIKI_MED_ICON_MARKUP .. workshopCategory .. "]]"
local categoryIconFilename = ICON_FILENAMES_CATEGORIES[workshopCategory]
innerTable:tag("tr"):tag("th"):wikitext(TITLE_CATEGORY):done()
if categoryIconFilename ~= nil then
:tag("td"):wikitext(catIcon .. NBSP .. workshopCategory):done():done():newline()
local categoryIconPart = "[[File:" .. categoryIconFilename .. WIKIMARKUP_MED_ICON_ALT .. workshopCategory .. "]]"
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
:tag("th"):wikitext(TITLE_CATEGORY):done()
:tag("td"):wikitext(categoryIconPart .. NBSP .. workshopCategory):done()
:done():newline()
end
end
end
if workshopSizeX and workshopSizeY then
-- Expand recipes into a small table
innerTable:tag("tr"):tag("th"):wikitext(TITLE_SIZE):done()
if workshopRecipesTable and #workshopRecipesTable > 0 then
:tag("td"):wikitext(workshopSizeX .. " × " .. workshopSizeY):done():done():newline()
local recipesRows = tostring( writeRecipesRows(workshopRecipesTable) )
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
:tag("th"):wikitext(TITLE_RECIPES):done()
:newline()
:tag("td"):wikitext(recipesRows):done()
:done():newline()
end
end
if workshopIsMovable then
-- Since false is a valid value, have to directly check if it's nil.
local isMovable = workshopIsMovable == "TRUE"
if workshopIsMovable ~= nil then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_MOVABLE):done()
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
:tag("td"):wikitext( isMovable and "Yes" or "No" ):done():done():newline()
:tag("th"):wikitext(TITLE_MOVABLE):done()
:tag("td"):wikitext( workshopIsMovable and "Yes" or "No" ):done()
:done():newline()
end
end
if workshopIsEssential then
if workshopIsEssential ~= nil then
local isEssential = workshopIsEssential == "TRUE"
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
innerTable:tag("tr"):tag("th"):wikitext(TITLE_ESSENTIAL):done()
:tag("th"):wikitext(TITLE_ESSENTIAL):done()
:tag("td"):wikitext( isEssential and "Yes" or "No" ):done():done():newline()
:tag("td"):wikitext( workshopIsEssential and "Yes" or "No" ):done()
:done():newline()
end
-- Combine sizes into one line
if workshopSizeX ~= nil and workshopSizeY ~= nil then
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
:tag("th"):wikitext(TITLE_SIZE):done()
:tag("td"):wikitext(workshopSizeX .. " × " .. workshopSizeY):done()
:done():newline()
end
end
if workshopStorageCap then
if workshopStorageCap then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_STORAGE_CAPACITY):done()
innerTable:tag("tr")
:tag("td"):wikitext(workshopStorageCap):done():done():newline()
:tag("th"):wikitext(TITLE_STORAGE_CAPACITY):done()
:tag("td"):wikitext(workshopStorageCap):done()
:done():newline()
end
end
if workshopWorkplaces then
if workshopWorkplaces then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_WORKPLACES):done()
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
:tag("td"):wikitext(countWorkplaces(workshopWorkplaces)):done():done():newline()
:tag("th"):wikitext(TITLE_WORKPLACES):done()
:tag("td"):wikitext(workshopWorkplaces):done()
:done():newline()
end
end
if workshopConstructionTime then
if workshopConstructionTime then
innerTable:tag("tr"):tag("th"):wikitext(TITLE_CONSTRUCTION_TIME):done()
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
:tag("td"):wikitext(workshopConstructionTime):done():done():newline()
:tag("th"):wikitext(TITLE_CONSTRUCTION_TIME):done()
:tag("td"):wikitext(workshopConstructionTime):done()
:done():newline()
end
end
-- Need to build a separate table to display building materials
-- Need to build a separate table to display building materials
if workshopConstructionGoods then
if workshopRequiredGoods then
local matsTable = tostring(writeRequiredGoodsRows(workshopConstructionGoods))
local requiredGoodsRows = tostring( writeRequiredGoodsRows(workshopRequiredGoods) )
innerTable:tag("tr"):tag("th"):wikitext(TITLE_CONSTRUCTION_COST):done()
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
:newline()
:tag("th"):wikitext(TITLE_CONSTRUCTION_COST):done()
:tag("td"):newline():wikitext(matsTable):newline():done():done():newline()
:newline()
:tag("td"):newline():wikitext(requiredGoodsRows):newline():done()
:done():newline()
end
end
if workshopID then
wikiInfobox:newline()
innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
:tag("th"):wikitext(TITLE_ID):done()
if workshopDescription then
:tag("td"):wikitext(workshopID):done()
wikiInfobox:tag("tr"):tag("td"):wikitext(workshopDescription):done():done():newline()
:done():newline()
end
end
 
return wikiInfobox
-- Close the table.
innerTable:done():newline()
-- Close the table cell and row the inner table is in.
wikiInfobox:done():done():newline()
end
end




--endregion
--region Public methods


---
---
-- Steps through the provided workplace list and counts non-empty strings
-- Creates an html table to display the data in a floating box.
--
--
-- @param workshopWorkplaces the list of workplaces from the workshop data
-- @param frame the template's calling context
-- @return the number of non-empty strings
-- @return wiki markup for the box
function countWorkplaces(workshopWorkplaces)
function Shopbox.infobox(frame)
local count = 0
-- Every workshop must have a name.
local workshopName = frame.args[ARG_WORKSHOP_NAME]
for _, place in ipairs(workshopWorkplaces) do
if not workshopName or workshopName == "" then
if place ~= "" then
return "You must specify the name of the workshop. See [[Template:Shopbox]] for examples."
count = count + 1
end
end
 
-- Use this method here to check to see whether the provided name is valid.
if not WorkshopsData.getAllDataForWorkshop(workshopName) then
return "Shopbox can't find the specified workshop: " .. workshopName .. "."
end
end
return count
end


-- Additional template parameters would go here.


-- Get the data to display.
local workshopDescription = WorkshopsData.getWorkshopDescription(workshopName)
local workshopIconFilename = WorkshopsData.getWorkshopIcon(workshopName)


---
-- Make the top of the infobox that every workshop has...
-- Lays out an inner, no-styling table to display construction goods.
local wikiInfobox = mw.html.create("table"):css(CSS_INLINE_INFOBOX):newline()
--
-- with a title...
-- @param
wikiInfobox:tag("tr")
function writeRequiredGoodsRows(requiredGoods)
:tag("th"):wikitext(workshopName):done()
:done():newline()
local matsTable = mw.html.create("table")
-- and a subtitle, showing the functional category...
matsTable:newline()
wikiInfobox:tag("tr")
:tag("td"):wikitext(BUILDING_CATEGORY):done()
for _, matsGroup in ipairs(requiredGoods) do
:done():newline()
-- and a large icon.
-- Skip empty groups
if workshopIconFilename then
if #matsGroup > 0 then
wikiInfobox:tag("tr")
:tag("td"):wikitext("[[File:" .. workshopIconFilename .. "]]"):done()
local stackSize = matsGroup[1]
:done():newline()
local materialID = matsGroup[2]
wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
:tag("td"):wikitext(workshopName .. ", as seen in-game"):done()
matsTable:tag("tr")
:tag("td"):wikitext(BOLD .. stackSize .. BOLD):done()
:tag("td"):wikitext(writeConstructionMatsCellContent(materialID)):done()
:done():newline()
:done():newline()
end
end
end
return matsTable
makeInnerTable(wikiInfobox, workshopName)
end


 
-- Finish with the flavor text.
 
if workshopDescription then
---
wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
-- Formats the information about the construction material with an icon and its
:tag("td"):wikitext(workshopDescription):done()
-- display name
:done():newline()
--
end
-- @param materialID the good whose icon and name to show
-- @return formatted string of wiki markup
function writeConstructionMatsCellContent(materialID)
local matName, matIconFilename = RecipeData.getGoodNameAndIcon(materialID)
return wikiInfobox
local iconMarkup = "[[File:" .. matIconFilename .. ".png" .. WIKI_MED_ICON_MARKUP .. matName .. "|link=" .. matName .. "]]"
local linkMarkup = "[[" .. matName .. "]]"
return iconMarkup .. NBSP .. linkMarkup
end
 
 
 
function toDecimal(value)
return string.format(PATTERN_FORMAT_TWO_DECIMALS, value)
end
end


 
--endregion


return Shopbox
return Shopbox

Revision as of 20:33, 19 November 2023

Documentation for this module may be created at Module:Shopbox/doc

---
--- Module to create an infobox for displaying on a workshop's wiki page.
---
--- Shows the facts from the data about the specified workshop in an easy-to-
--- read table, with a large icon, information, categories, and the flavor
--- text.
---
--- This should be invoked from Template:Shopbox with the parameter of the
--- workshop whose infobox should be shown. See the template documentation for
--- more information about parameters and errors and to see examples.
---
--- @module Shopbox
local Shopbox = {}



local WorkshopsData = require("Module:WorkshopsData")
local ResourceLink = require("Module:ResourceLink")



--region Private constants

-- External templates
local TEMPLATE_RL_ICON = "med"

-- Template parameters
local ARG_WORKSHOP_NAME = "name"

-- Infobox header labels
local TITLE_ID = "ID"
local TITLE_CATEGORY = "Toolbar Category"
local TITLE_SIZE = "Building Size"
local TITLE_MOVABLE = "Movable"
local TITLE_ESSENTIAL = "Initially Essential"
local TITLE_STORAGE_CAPACITY = "Storage Capacity"
local TITLE_CONSTRUCTION_TIME = "Construction Time"
local TITLE_CONSTRUCTION_COST = "Construction Cost"
local TITLE_WORKPLACES = "Workplaces"
local TITLE_RECIPES = "Recipes"

-- Subheading for all the buildings from this template.
local BUILDING_CATEGORY = "Production Building"

local INDEX_REQ_STACK_SIZE = "stackSize"
local INDEX_REQ_GOOD_ID = "goodID"

local BOLD = "'''"
local NBSP = " "
local WIKIMARKUP_MED_ICON_ALT = "|32px|alt="

local CSS_INLINE_INFOBOX = {
	["float"] = "right", 
	["clear"] = "right", 
	["width"] = "256px", 
	["border"] = "1px solid #a2a9b1", 
	["border-spacing"] = "3px",
	["border-collapse"] = "collapse",
	["margin"] = "0.5em 0 0.5em 1em"
}
local CSS_INLINE_INFOBOX_INNER_TABLE = {
	["border-collapse"] = "collapse"
}
local CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER = {
	["border-bottom"] = "1px solid #a2a9b1"
}
local CSS_INLINE_INFOBOX_TR_TOP_DIVIDER = {
	["border-top"] = "1px solid #a2a9b1"
}

-- Only two valid construction toolbar categories for buildings from this
-- template.
local ICON_FILENAMES_CATEGORIES = {
	["Food Production"] = "Construct Food Production.png",
	["Industry"] = "Construct Industry.png"
}

--endregion



--region Private methods

---
--- A shortcut method that redirects to ResourceLink to build links to goods
--- with their IDs from the tables of required goods.
---
---@param goodID string the ID of the good to link to
---@return string wikimarkup representing an icon and link
local function resourceLink(goodID)

	return ResourceLink.resourceLinkWithID(goodID, TEMPLATE_RL_ICON)
end



---
--- Lays out an inner, no-styling table to display construction goods with
--- their stack sizes.
---
--- @param requiredGoods table of required goods, with stack sizes and IDs
--- @return string wikimarkup for a table of required goods to insert in other markup
local function writeRequiredGoodsRows(requiredGoods)

	if not requiredGoods or #requiredGoods == 0 then
		error("Cannot write content with provided table of required goods.")
	end

	local requiredGoodsRows =  mw.html.create("table")
	requiredGoodsRows:newline()

	for _, requiredGroup in ipairs(requiredGoods) do

		local stackSize = requiredGroup[INDEX_REQ_STACK_SIZE]
		local goodID = requiredGroup[INDEX_REQ_GOOD_ID]

		requiredGoodsRows:tag("tr")
				:tag("td"):wikitext(BOLD .. stackSize .. BOLD):done()
				:tag("td"):wikitext(resourceLink(goodID)):done()
			:done():newline()
	end

	return requiredGoodsRows
end



---
--- Lays out the provided recipe IDs in a small list.
---
---@param recipes table of recipe IDs
---@return string wiki markup of a list of those IDs
local function writeRecipesRows(recipes)

	if not recipes or #recipes == 0 then
		error("Cannot write content with provided table of recipes.")
	end

	local recipeRows = mw.html.create("ul")
	recipeRows:newline()

	for _, recipeID in ipairs(recipes) do
		-- Temporarily, just write the ID of the recipe.
		recipeRows:tag("li"):wikitext(recipeID):done():newline()
	end

	recipeRows:done()

	return recipeRows
end



---
--- Builds using the provided wikiInfobox a subtable to lay out headers and
--- fields.
---
---@param wikiInfobox table mw.html object into which we're building this
---@param workshopName string the name of the good the infobox is about
---@return table the Mediawiki html object into which we've been adding things
local function makeInnerTable(wikiInfobox, workshopName)

	-- Grab the data we'll use to populate this.
	local workshopID = WorkshopsData.getWorkshopID(workshopName)
	local workshopCategory = WorkshopsData.getWorkshopCategory(workshopName)
	local workshopSizeX, workshopSizeY = WorkshopsData.getWorkshopSize(workshopName)
	local workshopIsMovable = WorkshopsData.isWorkshopMovable(workshopName)
	local workshopIsEssential = WorkshopsData.isWorkshopInitiallyEssential(workshopName)
	local workshopStorageCap = WorkshopsData.getWorkshopStorage(workshopName)
	local workshopConstructionTime = WorkshopsData.getWorkshopConstructionTime(workshopName)
	local workshopRequiredGoods = WorkshopsData.getWorkshopRequiredGoods(workshopName)
	local workshopRecipesTable = WorkshopsData.getWorkshopRecipes(workshopName)
	local workshopWorkplacesTable = WorkshopsData.getWorkshopWorkplaces(workshopName)

	-- Only need to count these, just do it right away.
	local workshopWorkplaces
	if not workshopWorkplacesTable then
		workshopWorkplaces = #workshopWorkplacesTable
	else
		workshopWorkplaces = nil
	end

	-- Start the inner table
	local innerTable = wikiInfobox:tag("tr"):tag("td"):newline():tag("table"):css(CSS_INLINE_INFOBOX_INNER_TABLE)
	innerTable:newline()

	-- Additional parameters would go here.

	-- Construction toolbar category.
	if workshopCategory then
		local categoryIconFilename = ICON_FILENAMES_CATEGORIES[workshopCategory]
		if categoryIconFilename ~= nil then
			local categoryIconPart = "[[File:" .. categoryIconFilename .. WIKIMARKUP_MED_ICON_ALT .. workshopCategory .. "]]"
			innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
					:tag("th"):wikitext(TITLE_CATEGORY):done()
					:tag("td"):wikitext(categoryIconPart .. NBSP .. workshopCategory):done()
				:done():newline()
		end
	end
	-- Expand recipes into a small table
	if workshopRecipesTable and #workshopRecipesTable > 0 then
		local recipesRows = tostring( writeRecipesRows(workshopRecipesTable) )
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
				:tag("th"):wikitext(TITLE_RECIPES):done()
				:newline()
				:tag("td"):wikitext(recipesRows):done()
			:done():newline()
	end
	-- Since false is a valid value, have to directly check if it's nil.
	if workshopIsMovable ~= nil then
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
				:tag("th"):wikitext(TITLE_MOVABLE):done()
				:tag("td"):wikitext( workshopIsMovable and "Yes" or "No" ):done()
			:done():newline()
	end
	if workshopIsEssential ~= nil then
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
				:tag("th"):wikitext(TITLE_ESSENTIAL):done()
				:tag("td"):wikitext( workshopIsEssential and "Yes" or "No" ):done()
			:done():newline()
	end
	-- Combine sizes into one line
	if workshopSizeX ~= nil and workshopSizeY ~= nil then
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
				:tag("th"):wikitext(TITLE_SIZE):done()
				:tag("td"):wikitext(workshopSizeX .. " × " .. workshopSizeY):done()
			:done():newline()
	end
	if workshopStorageCap then
		innerTable:tag("tr")
				:tag("th"):wikitext(TITLE_STORAGE_CAPACITY):done()
				:tag("td"):wikitext(workshopStorageCap):done()
			:done():newline()
	end
	if workshopWorkplaces then
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
				:tag("th"):wikitext(TITLE_WORKPLACES):done()
				:tag("td"):wikitext(workshopWorkplaces):done()
			:done():newline()
	end
	if workshopConstructionTime then
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
				:tag("th"):wikitext(TITLE_CONSTRUCTION_TIME):done()
				:tag("td"):wikitext(workshopConstructionTime):done()
			:done():newline()
	end
	-- Need to build a separate table to display building materials
	if workshopRequiredGoods then
		local requiredGoodsRows = tostring( writeRequiredGoodsRows(workshopRequiredGoods) )
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
				:tag("th"):wikitext(TITLE_CONSTRUCTION_COST):done()
				:newline()
				:tag("td"):newline():wikitext(requiredGoodsRows):newline():done()
			:done():newline()
	end
	if workshopID then
		innerTable:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
				:tag("th"):wikitext(TITLE_ID):done()
				:tag("td"):wikitext(workshopID):done()
			:done():newline()
	end

	-- Close the table.
	innerTable:done():newline()
	-- Close the table cell and row the inner table is in.
	wikiInfobox:done():done():newline()
end


--endregion



--region Public methods

---
-- Creates an html table to display the data in a floating box.
--
-- @param frame the template's calling context
-- @return wiki markup for the box
function Shopbox.infobox(frame)
	
	-- Every workshop must have a name.
	local workshopName = frame.args[ARG_WORKSHOP_NAME]
	if not workshopName or workshopName == "" then
		return "You must specify the name of the workshop. See [[Template:Shopbox]] for examples."
	end

	-- Use this method here to check to see whether the provided name is valid.
	if not WorkshopsData.getAllDataForWorkshop(workshopName) then
		return "Shopbox can't find the specified workshop: " .. workshopName .. "."
	end

	-- Additional template parameters would go here.

	-- Get the data to display.
	local workshopDescription = WorkshopsData.getWorkshopDescription(workshopName)
	local workshopIconFilename = WorkshopsData.getWorkshopIcon(workshopName)

	-- Make the top of the infobox that every workshop has...
	local wikiInfobox = mw.html.create("table"):css(CSS_INLINE_INFOBOX):newline()
	-- with a title...
	wikiInfobox:tag("tr")
			:tag("th"):wikitext(workshopName):done()
		:done():newline()
	-- and a subtitle, showing the functional category...
	wikiInfobox:tag("tr")
			:tag("td"):wikitext(BUILDING_CATEGORY):done()
		:done():newline()
	-- and a large icon.
	if workshopIconFilename then
		wikiInfobox:tag("tr")
				:tag("td"):wikitext("[[File:" .. workshopIconFilename .. "]]"):done()
			:done():newline()
		wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_BOTTOM_DIVIDER)
				:tag("td"):wikitext(workshopName .. ", as seen in-game"):done()
			:done():newline()
	end
	
	makeInnerTable(wikiInfobox, workshopName)

	-- Finish with the flavor text.
	if workshopDescription then
		wikiInfobox:tag("tr"):css(CSS_INLINE_INFOBOX_TR_TOP_DIVIDER)
				:tag("td"):wikitext(workshopDescription):done()
			:done():newline()
	end
	
	return wikiInfobox
end

--endregion

return Shopbox