<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.getinterspace.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AInfobox</id>
	<title>Module:Infobox - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.getinterspace.com/index.php?action=history&amp;feed=atom&amp;title=Module%3AInfobox"/>
	<link rel="alternate" type="text/html" href="https://wiki.getinterspace.com/index.php?title=Module:Infobox&amp;action=history"/>
	<updated>2026-04-21T18:45:24Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://wiki.getinterspace.com/index.php?title=Module:Infobox&amp;diff=85&amp;oldid=prev</id>
		<title>Dreitona: Created page with &quot;-- version 0.1.6  -------------------------------------- -- User settings, you can modify these --------------------------------------  -- if you want to not always use divs in your wiki (as opposed to tables), you can change this default -- just remember to change it back each time you update from the main &quot;branch&quot; on the support wiki! -- you can also control it per infobox with `|useDivs=yes` or `|useDivs=no` local USE_DIVS = true -- `false` or `true`  -- default value...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.getinterspace.com/index.php?title=Module:Infobox&amp;diff=85&amp;oldid=prev"/>
		<updated>2025-01-15T23:44:07Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;-- version 0.1.6  -------------------------------------- -- User settings, you can modify these --------------------------------------  -- if you want to not always use divs in your wiki (as opposed to tables), you can change this default -- just remember to change it back each time you update from the main &amp;quot;branch&amp;quot; on the support wiki! -- you can also control it per infobox with `|useDivs=yes` or `|useDivs=no` local USE_DIVS = true -- `false` or `true`  -- default value...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- version 0.1.6&lt;br /&gt;
&lt;br /&gt;
--------------------------------------&lt;br /&gt;
-- User settings, you can modify these&lt;br /&gt;
--------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- if you want to not always use divs in your wiki (as opposed to tables), you can change this default&lt;br /&gt;
-- just remember to change it back each time you update from the main &amp;quot;branch&amp;quot; on the support wiki!&lt;br /&gt;
-- you can also control it per infobox with `|useDivs=yes` or `|useDivs=no`&lt;br /&gt;
local USE_DIVS = true -- `false` or `true`&lt;br /&gt;
&lt;br /&gt;
-- default value to show if a param is missing in some but not all tabs.&lt;br /&gt;
-- set to `nil` (not in quotes) to remove such rows altogether in the tabs where they&amp;#039;re missing&lt;br /&gt;
local TABBED_NONEXIST = nil -- `&amp;#039;&amp;#039;` or `nil` or `&amp;#039;N/A&amp;#039;` etc. Don&amp;#039;t put nil in quotes.&lt;br /&gt;
&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
-- Do not modify anything below this line unless you know what you&amp;#039;re doing&lt;br /&gt;
---------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local h = {}&lt;br /&gt;
local p = {}&lt;br /&gt;
local hooks = {}&lt;br /&gt;
&lt;br /&gt;
function p.arraymap(frame)&lt;br /&gt;
	-- a lua implementation of Page Forms&amp;#039; arraymap&lt;br /&gt;
	local args = h.overwrite()&lt;br /&gt;
	local items = h.split(args[1], args[2] or &amp;#039;,&amp;#039;)&lt;br /&gt;
	for i, item in ipairs(items) do&lt;br /&gt;
		items[i] = args[4]:gsub(args[3], item)&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(items, args[5] or &amp;#039;,&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.preprocess(frame)&lt;br /&gt;
    return frame:preprocess(frame.args[1] or frame:getParent().args[1])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	h.registerHooks()&lt;br /&gt;
	h.increment()&lt;br /&gt;
	local args = h.overwrite()&lt;br /&gt;
	local sep = args.sep or &amp;#039;,&amp;#039;&lt;br /&gt;
	h.castArgs(args, sep)&lt;br /&gt;
    h.setMainImage(args.images[1])&lt;br /&gt;
    -- suggest to use HIDDENCAT here; will be used for maintenance &amp;amp; gadget imports&lt;br /&gt;
	return h.makeInfobox(args, sep), &amp;#039;[[Category:Pages with DRUID infoboxes]]&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.registerHooks()&lt;br /&gt;
	if not mw.title.new(&amp;#039;Module:Infobox/Hooks&amp;#039;).exists then return end&lt;br /&gt;
	hooks = require(&amp;#039;Module:Infobox/Hooks&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.runHook(key, ...)&lt;br /&gt;
	if hooks[key] then&lt;br /&gt;
		hooks[key](...)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.increment()&lt;br /&gt;
	-- optional use of VariablesLua for better compatibility&lt;br /&gt;
	local VariablesLua = mw.ext.VariablesLua&lt;br /&gt;
	if VariablesLua == nil then&lt;br /&gt;
		h.counter = mw.getCurrentFrame():callParserFunction(&amp;#039;#var&amp;#039;, {&amp;#039;DRUID_INFOBOX_ID&amp;#039;, 0}) + 1&lt;br /&gt;
		mw.getCurrentFrame():callParserFunction(&amp;#039;#vardefine&amp;#039;, {&amp;#039;DRUID_INFOBOX_ID&amp;#039;, h.counter})&lt;br /&gt;
	else&lt;br /&gt;
		h.counter = VariablesLua.var(&amp;#039;DRUID_INFOBOX_ID&amp;#039;, 0) + 1&lt;br /&gt;
		VariablesLua.vardefine(&amp;#039;DRUID_INFOBOX_ID&amp;#039;, h.counter)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.castArgs(args, sep)&lt;br /&gt;
	h.runHook(&amp;#039;onCastArgsStart&amp;#039;, args, sep, args.kind)&lt;br /&gt;
	args.tabs = h.split(args.tabs or args.image_labels, sep)&lt;br /&gt;
	args.images = h.getImages(args, sep)&lt;br /&gt;
	args.sections = h.split(args.sections, sep)&lt;br /&gt;
	for _, section in ipairs(args.sections) do&lt;br /&gt;
		args[section] = h.split(args[section], sep)&lt;br /&gt;
		args[section .. &amp;#039;_tabs&amp;#039;] = h.split(args[section .. &amp;#039;_tabs&amp;#039;], sep)&lt;br /&gt;
		if #args.tabs &amp;gt; 0 and #args[section .. &amp;#039;_tabs&amp;#039;] &amp;gt; 0 then&lt;br /&gt;
			error((&amp;#039;You cannot specify |tabs= and |%s= at the same time, please pick one&amp;#039;):format(section .. &amp;#039;_tabs&amp;#039;))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if args.useDivs then&lt;br /&gt;
		USE_DIVS = h.castBool(args.useDivs)&lt;br /&gt;
	end&lt;br /&gt;
	-- this would be in the outer scope, but we&amp;#039;re hiding it&lt;br /&gt;
	h.entityType = USE_DIVS and &amp;#039;div&amp;#039; or &amp;#039;table&amp;#039; -- key of h.htmlEntities&lt;br /&gt;
	h.runHook(&amp;#039;onCastArgsEnd&amp;#039;, args, sep, args.kind)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.getImages(args, sep)&lt;br /&gt;
	if args.image and not args.images then&lt;br /&gt;
		args.images = args.image&lt;br /&gt;
	end&lt;br /&gt;
	if args.images then&lt;br /&gt;
		return h.split(args.images, sep)&lt;br /&gt;
	end&lt;br /&gt;
	if not args.tabs then return {} end&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for _, key in ipairs(args.tabs) do&lt;br /&gt;
		if args[key .. &amp;#039;_image&amp;#039;] then&lt;br /&gt;
			ret[#ret+1] = args[key .. &amp;#039;_image&amp;#039;]&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.setMainImage(file)&lt;br /&gt;
	if h.counter &amp;gt; 1 then return end&lt;br /&gt;
    if not file then return end&lt;br /&gt;
    local fileText = file:gsub(&amp;#039;.-:&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
	fileText = fileText:gsub(&amp;#039;^([^|%]]+).*&amp;#039;, &amp;#039;%1&amp;#039;)&lt;br /&gt;
	-- setmainimage is guaranteed to exist on wiki.gg but may not exist on other wikis&lt;br /&gt;
	-- it&amp;#039;s not a crucial piece of functionality so we&amp;#039;ll fail silently if it doesn&amp;#039;t exist&lt;br /&gt;
	pcall(function() mw.getCurrentFrame():callParserFunction{&lt;br /&gt;
		name = &amp;#039;#setmainimage&amp;#039;,&lt;br /&gt;
		args = { fileText },&lt;br /&gt;
	} end)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.makeInfobox(args, sep)&lt;br /&gt;
	local out = mw.html.create(h.getTag(&amp;#039;container&amp;#039;))&lt;br /&gt;
		:addClass(&amp;#039;druid-infobox&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-container&amp;#039;)&lt;br /&gt;
		:addClass(args.class) -- warning: class can be nil, don&amp;#039;t concat anything&lt;br /&gt;
		:attr(&amp;#039;id&amp;#039;, args.id or (&amp;#039;druid-container-&amp;#039; .. h.counter))&lt;br /&gt;
	if args.kind then out:addClass(&amp;#039;druid-container-&amp;#039; .. h.escape(args.kind)) end&lt;br /&gt;
	h.printTitle(out, args)&lt;br /&gt;
	h.printImages(out, args.images, args)&lt;br /&gt;
	for _, section in ipairs(args.sections) do&lt;br /&gt;
		-- cannot begin tagging here because we don&amp;#039;t know if any applicable args are present&lt;br /&gt;
		local cols = args[section .. &amp;#039;_columns&amp;#039;]&lt;br /&gt;
		local makeSection = cols and h.makeGridSection or h.makeSection&lt;br /&gt;
		out:node(makeSection(section, args[section], args, tonumber(cols)))&lt;br /&gt;
	end&lt;br /&gt;
	return out&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printTitle(out, args)&lt;br /&gt;
	local tabs = args.tabs&lt;br /&gt;
	if not tabs or #tabs == 0 then&lt;br /&gt;
		h.printSimpleTitle(out, args)&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	if not h.hasComplexData(&amp;#039;title&amp;#039;, tabs, args) then&lt;br /&gt;
		h.printSimpleTitle(out, args)&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	local node = h.printTitleWrapper(out)&lt;br /&gt;
	h.printTabbedDataItem(node, &amp;#039;title&amp;#039;, tabs, args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printSimpleTitle(out, args)&lt;br /&gt;
	if args.title then&lt;br /&gt;
		local node = h.printTitleWrapper(out)&lt;br /&gt;
		node:wikitext(args.title)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printTitleWrapper(out)&lt;br /&gt;
	return out:tag(h.getTag(&amp;#039;titleOuter&amp;#039;))&lt;br /&gt;
		:tag(h.getTag(&amp;#039;titleInner&amp;#039;))&lt;br /&gt;
			:addClass(&amp;#039;druid-title&amp;#039;)&lt;br /&gt;
			:attr(&amp;#039;colspan&amp;#039;, 2)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printTabbedDataItem(node, item, tabs, args)&lt;br /&gt;
	-- hasData isn&amp;#039;t used in the title case but we will need to track this&lt;br /&gt;
	-- when we&amp;#039;re printing section data later on&lt;br /&gt;
	-- so we&amp;#039;ll just track it always&lt;br /&gt;
	local hasData = false&lt;br /&gt;
	for i, label in ipairs(tabs) do&lt;br /&gt;
		local div = node:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-toggleable-data&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-toggleable&amp;#039;)&lt;br /&gt;
			:attr(&amp;#039;data-druid&amp;#039;, h.counter .. &amp;#039;-&amp;#039; .. i)&lt;br /&gt;
			:attr(&amp;#039;data-druid-tab-key&amp;#039;, label)&lt;br /&gt;
		if h.getTabbedContent(args, label, item) then&lt;br /&gt;
			hasData = true&lt;br /&gt;
			div:wikitext(&amp;#039;\n\n&amp;#039; .. h.getTabbedContent(args, label, item))&lt;br /&gt;
			div:addClass(&amp;#039;druid-toggleable-data-nonempty&amp;#039;)&lt;br /&gt;
		else&lt;br /&gt;
			div:addClass(&amp;#039;druid-toggleable-data-empty&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if i == 1 then div:addClass(&amp;#039;focused&amp;#039;) end&lt;br /&gt;
	end&lt;br /&gt;
	return hasData&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printImages(out, images, args)&lt;br /&gt;
	if #images == 0 and #args.tabs == 0 then return end&lt;br /&gt;
	-- burden is on the user to format this as an image. this should be done in the infobox template,&lt;br /&gt;
	-- with something like |image={{#if:{{{image|}}}|[[File:{{{image|}}}{{!}}300px{{!}}link=]]}}&lt;br /&gt;
	local td = out:tag(h.getTag(&amp;#039;section&amp;#039;))&lt;br /&gt;
		:addClass(&amp;#039;druid-section-container&amp;#039;)&lt;br /&gt;
		:tag(h.getTag(&amp;#039;cell&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;colspan&amp;#039;, 2)&lt;br /&gt;
	local tabs = args.tabs&lt;br /&gt;
	local tabTexts = h.getImageTabTexts(tabs, images, args)&lt;br /&gt;
	h.printTabs(td, tabs, tabTexts, false, args)&lt;br /&gt;
	if #images == 0 then return end&lt;br /&gt;
	if #images == 1 then&lt;br /&gt;
		td:addClass(&amp;#039;druid-main-image&amp;#039;)&lt;br /&gt;
			:wikitext(images[1])&lt;br /&gt;
		if args.caption then&lt;br /&gt;
			td:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
				:addClass(&amp;#039;druid-main-image-caption&amp;#039;)&lt;br /&gt;
				:wikitext(args.caption)&lt;br /&gt;
		end&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	td:addClass(&amp;#039;druid-main-images&amp;#039;)&lt;br /&gt;
	local imagesContainer = td:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-main-images-files&amp;#039;)&lt;br /&gt;
	for i, image in ipairs(images) do&lt;br /&gt;
		local container = imagesContainer:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-main-images-file&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-toggleable&amp;#039;)&lt;br /&gt;
			:attr(&amp;#039;data-druid&amp;#039;, h.counter .. &amp;#039;-&amp;#039; .. i)&lt;br /&gt;
			:wikitext(image)&lt;br /&gt;
			:attr(&amp;#039;data-druid-tab-key&amp;#039;, tabs[i])&lt;br /&gt;
		local labelText&lt;br /&gt;
		if tabs[i] then&lt;br /&gt;
			labelText = args[tabs[i] .. &amp;#039;_label&amp;#039;] or tabs[i]&lt;br /&gt;
		else&lt;br /&gt;
			labelText = &amp;#039;[[Category:Infoboxes missing image labels]]Image &amp;#039; .. i&lt;br /&gt;
		end&lt;br /&gt;
		if args[labelText .. &amp;#039;_caption&amp;#039;] then&lt;br /&gt;
			container:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
				:addClass(&amp;#039;druid-main-images-caption&amp;#039;)&lt;br /&gt;
				:wikitext(args[labelText .. &amp;#039;_caption&amp;#039;])&lt;br /&gt;
		end&lt;br /&gt;
		if i == 1 then&lt;br /&gt;
			container:addClass(&amp;#039;focused&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.getImageTabTexts(tabs, images, args)&lt;br /&gt;
	if #tabs == 0 and #images &amp;lt;= 1 then return {} end&lt;br /&gt;
	local texts = {}&lt;br /&gt;
	local i = 1&lt;br /&gt;
	while images[i] or tabs[i] do&lt;br /&gt;
		if tabs[i] then&lt;br /&gt;
			texts[i] = args[tabs[i] .. &amp;#039;_label&amp;#039;] or tabs[i]&lt;br /&gt;
		else&lt;br /&gt;
			texts[i] = &amp;#039;[[Category:Infoboxes missing image labels]]Image &amp;#039; .. i&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return texts&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printTabs(td, tabs, texts, isSection, args)&lt;br /&gt;
	if #texts == 0 then return end&lt;br /&gt;
	local container = td:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-main-images-labels&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-tabs&amp;#039;)&lt;br /&gt;
	if isSection then&lt;br /&gt;
		container:addClass(&amp;#039;druid-section-tabs&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	for i, item in ipairs(tabs) do&lt;br /&gt;
		local label = container:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-main-images-label&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-tab&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-toggleable&amp;#039;)&lt;br /&gt;
			:attr(&amp;#039;data-druid&amp;#039;, h.counter .. &amp;#039;-&amp;#039; .. i)&lt;br /&gt;
			:wikitext(texts[i])&lt;br /&gt;
			:attr(&amp;#039;data-druid-tab-key&amp;#039;, item)&lt;br /&gt;
		if isSection then&lt;br /&gt;
			label:addClass(&amp;#039;druid-section-tab&amp;#039;)&lt;br /&gt;
		else&lt;br /&gt;
			label:addClass(&amp;#039;druid-title-tab&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		if i == 1 then&lt;br /&gt;
			label:addClass(&amp;#039;focused&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		-- this can be null, don&amp;#039;t concat anything here&lt;br /&gt;
		label:addClass(args[item .. &amp;#039;_class&amp;#039;])&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.makeGridSection(section, sectionFields, args, numCols)&lt;br /&gt;
	local numItems = h.countItems(sectionFields, section, args)&lt;br /&gt;
	if numItems == 0 then return end&lt;br /&gt;
	local node = mw.html.create(h.getTag(&amp;#039;section&amp;#039;))&lt;br /&gt;
		:addClass(&amp;#039;druid-section-container&amp;#039;)&lt;br /&gt;
	h.printSectionHeader(node, section, args)&lt;br /&gt;
	h.printSectionTabs(node, section, args)&lt;br /&gt;
	local tr = node:tag(h.getTag(&amp;#039;row&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;data-druid-section-row&amp;#039;, h.escape(section))&lt;br /&gt;
	if args[section .. &amp;#039;_collapsed&amp;#039;] then&lt;br /&gt;
		tr:addClass(&amp;#039;druid-collapsed&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	local grid = tr:tag(h.getTag(&amp;#039;cell&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;colspan&amp;#039;, 2)&lt;br /&gt;
		:addClass(&amp;#039;druid-grid-section&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-grid-section-&amp;#039; .. h.escape(section))&lt;br /&gt;
		:addClass(args[section .. &amp;#039;_class&amp;#039;]) -- warning: class can be nil, don&amp;#039;t concat anything&lt;br /&gt;
		:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-grid&amp;#039;)&lt;br /&gt;
	local row, col, i = 1, 1, 1&lt;br /&gt;
	local sizeOfLastRow = numItems % numCols&lt;br /&gt;
	local lcm = h.getNumGridCols(numItems, sizeOfLastRow, numCols)&lt;br /&gt;
	grid:css(&amp;#039;grid-template-columns&amp;#039;, (&amp;#039;repeat(%s, 1fr)&amp;#039;):format(lcm))&lt;br /&gt;
	local size = lcm / numCols&lt;br /&gt;
	for _, item in ipairs(sectionFields) do&lt;br /&gt;
		local node = mw.html.create(&amp;#039;div&amp;#039;)&lt;br /&gt;
		local shouldPrint = h.printData(node, item, section, args)&lt;br /&gt;
		if shouldPrint then&lt;br /&gt;
			if i == numItems - sizeOfLastRow + 1 then&lt;br /&gt;
				size = lcm / sizeOfLastRow&lt;br /&gt;
			end&lt;br /&gt;
			i = i + 1&lt;br /&gt;
			local gStart = (col - 1) * size + 1&lt;br /&gt;
			local gEnd = (col) * size + 1&lt;br /&gt;
			local itemContainer = grid:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
				:addClass(&amp;#039;druid-grid-item&amp;#039;)&lt;br /&gt;
				:addClass(&amp;#039;druid-grid-item-&amp;#039; .. h.escape(item))&lt;br /&gt;
				:addClass(args[item .. &amp;#039;_class&amp;#039;]) -- warning: class can be nil, don&amp;#039;t concat anything&lt;br /&gt;
				:css(&amp;#039;grid-column&amp;#039;, (&amp;#039;%s / %s&amp;#039;):format(gStart, gEnd))&lt;br /&gt;
				:css(&amp;#039;grid-row&amp;#039;, row)&lt;br /&gt;
			if not h.castBool(args[item .. &amp;#039;_nolabel&amp;#039;]) then&lt;br /&gt;
				h.printLabel(itemContainer:tag(&amp;#039;div&amp;#039;), item, args)&lt;br /&gt;
			end&lt;br /&gt;
			itemContainer:node(node)&lt;br /&gt;
			if col == numCols then&lt;br /&gt;
				row = row + 1&lt;br /&gt;
				col = 1&lt;br /&gt;
			else&lt;br /&gt;
				col = col + 1&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return node&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.makeSection(section, sectionFields, args)&lt;br /&gt;
	if section == &amp;#039;&amp;#039; then return end -- bruteforce fix for trailing commas&lt;br /&gt;
	local shouldPrint = false&lt;br /&gt;
	local container = mw.html.create(h.getTag(&amp;#039;section&amp;#039;))&lt;br /&gt;
		:addClass(&amp;#039;druid-section-container&amp;#039;)&lt;br /&gt;
		:addClass(args[section .. &amp;#039;_class&amp;#039;]) -- warning: class can be nil, don&amp;#039;t concat anything&lt;br /&gt;
	h.printSectionHeader(container, section, args)&lt;br /&gt;
	h.printSectionTabs(container, section, args)&lt;br /&gt;
	for _, item in ipairs(sectionFields) do&lt;br /&gt;
		local node = mw.html.create(h.getTag(&amp;#039;cell&amp;#039;))&lt;br /&gt;
		local shouldPrintItem = h.printData(node, item, section, args)&lt;br /&gt;
		if shouldPrintItem then&lt;br /&gt;
			shouldPrint = true&lt;br /&gt;
			local tr = container:tag(h.getTag(&amp;#039;row&amp;#039;))&lt;br /&gt;
				:addClass(&amp;#039;druid-row&amp;#039;)&lt;br /&gt;
				:addClass(&amp;#039;druid-row-&amp;#039; .. h.escape(item))&lt;br /&gt;
				:addClass(args[item .. &amp;#039;_class&amp;#039;]) -- warning: class can be nil, don&amp;#039;t concat anything&lt;br /&gt;
				:attr(&amp;#039;data-druid-section-row&amp;#039;, h.escape(section))&lt;br /&gt;
			if args[section .. &amp;#039;_collapsed&amp;#039;] then&lt;br /&gt;
				tr:addClass(&amp;#039;druid-collapsed&amp;#039;)&lt;br /&gt;
			end&lt;br /&gt;
			if h.castBool(args[item .. &amp;#039;_wide&amp;#039;]) or h.castBool(args[item .. &amp;#039;_nolabel&amp;#039;]) then&lt;br /&gt;
				node&lt;br /&gt;
					:attr(&amp;#039;colspan&amp;#039;, 2)&lt;br /&gt;
					:addClass(&amp;#039;druid-data-wide&amp;#039;)&lt;br /&gt;
			else&lt;br /&gt;
				h.printLabel(tr:tag(h.getTag(&amp;#039;label&amp;#039;)), item, args)&lt;br /&gt;
			end&lt;br /&gt;
			tr:node(node)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if not shouldPrint then return nil end&lt;br /&gt;
	return container&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.countItems(sectionFields, section, args)&lt;br /&gt;
	local numItems = 0&lt;br /&gt;
	for _, v in ipairs(sectionFields) do&lt;br /&gt;
		-- we aren&amp;#039;t actually printing here, but we&amp;#039;re finding out if we should print anything&lt;br /&gt;
		-- because we need the count of columns before we print anything in grid data&lt;br /&gt;
		if h.printData(mw.html.create(), v, section, args) then&lt;br /&gt;
			numItems = numItems + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return numItems&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.getNumGridCols(numItems, sizeOfLastRow, numCols)&lt;br /&gt;
	if not numCols then return numItems, 1 end&lt;br /&gt;
	if numItems &amp;lt; numCols then return numItems, 1 end&lt;br /&gt;
	if sizeOfLastRow == 0 then&lt;br /&gt;
		return numCols, 1&lt;br /&gt;
	end&lt;br /&gt;
	local a, b = sizeOfLastRow, numCols&lt;br /&gt;
	while b ~= 0 do&lt;br /&gt;
	    a, b = b, a % b&lt;br /&gt;
	end&lt;br /&gt;
	local lcm = sizeOfLastRow * numCols / a&lt;br /&gt;
	return lcm&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printLabel(node, item, args)&lt;br /&gt;
	return node&lt;br /&gt;
		:addClass(&amp;#039;druid-label&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-label-&amp;#039; .. h.escape(item))&lt;br /&gt;
		:wikitext(args[item .. &amp;#039;_display&amp;#039;] or args[item .. &amp;#039;_label&amp;#039;] or item)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printData(node, item, section, args)&lt;br /&gt;
	-- prints data to the node&lt;br /&gt;
	-- and also returns whether the item is nonempty or not&lt;br /&gt;
	local hasData = false&lt;br /&gt;
	local sectionTabs = args[section .. &amp;#039;_tabs&amp;#039;]&lt;br /&gt;
	local tabs = args.tabs&lt;br /&gt;
	if sectionTabs and #sectionTabs &amp;gt; 0 then&lt;br /&gt;
		tabs = sectionTabs&lt;br /&gt;
	end&lt;br /&gt;
	if not tabs or #tabs == 0 then&lt;br /&gt;
		return h.printSimpleData(node, item, args)&lt;br /&gt;
	end&lt;br /&gt;
	if not h.hasComplexData(item, tabs, args) then&lt;br /&gt;
		return h.printSimpleData(node, item, args)&lt;br /&gt;
	end&lt;br /&gt;
	hasData = hasData or h.printTabbedDataItem(node, item, tabs, args)&lt;br /&gt;
	if hasData then&lt;br /&gt;
		node:addClass(&amp;#039;druid-data&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	return hasData&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.getTabbedContent(args, label, item)&lt;br /&gt;
	return args[label .. &amp;#039;_&amp;#039; .. item] or args[item] or TABBED_NONEXIST&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printSimpleData(node, item, args)&lt;br /&gt;
	if args[item] and type(args[item]) ~= &amp;#039;string&amp;#039; then&lt;br /&gt;
		error((&amp;quot;Invalid use of field %s as both a section and a data value&amp;quot;):format(item))&lt;br /&gt;
	end&lt;br /&gt;
	if not args[item] then return false end&lt;br /&gt;
	node:addClass(&amp;#039;druid-data&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-data-&amp;#039; .. h.escape(item))&lt;br /&gt;
		:addClass(&amp;#039;druid-data-nonempty&amp;#039;)&lt;br /&gt;
		:wikitext(&amp;#039;\n\n&amp;#039; .. args[item])&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.hasComplexData(item, tabs, args)&lt;br /&gt;
	for _, v in ipairs(tabs) do&lt;br /&gt;
		if args[v .. &amp;#039;_&amp;#039; .. item] then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printSectionHeader(node, section, args)&lt;br /&gt;
	if h.castBool(args[section .. &amp;#039;_nolabel&amp;#039;]) then return end&lt;br /&gt;
	local tr = node:tag(h.getTag(&amp;#039;row&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;data-druid-section&amp;#039;, h.escape(section))&lt;br /&gt;
	local th = tr:tag(h.getTag(&amp;#039;sectionTitle&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;colspan&amp;#039;, 2)&lt;br /&gt;
		:addClass(&amp;#039;druid-section&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-section-&amp;#039; .. h.escape(section))&lt;br /&gt;
	if args[section .. &amp;#039;_collapsible&amp;#039;] then&lt;br /&gt;
		tr:addClass(&amp;#039;druid-collapsible&amp;#039;)&lt;br /&gt;
		if args[section .. &amp;#039;_collapsed&amp;#039;] then&lt;br /&gt;
			tr:addClass(&amp;#039;druid-collapsible-collapsed&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local emptySections = {}&lt;br /&gt;
	for _, label in ipairs(args.tabs) do&lt;br /&gt;
		local hasLabel = false&lt;br /&gt;
		for _, item in ipairs(args[section] or {}) do&lt;br /&gt;
			if h.getTabbedContent(args, label, item) then&lt;br /&gt;
				hasLabel = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if not hasLabel then emptySections[label] = true end&lt;br /&gt;
	end&lt;br /&gt;
	if not next(emptySections) then&lt;br /&gt;
		th:wikitext(args[section .. &amp;#039;_label&amp;#039;] or section)&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	for i, label in ipairs(args.tabs) do&lt;br /&gt;
		local div = th:tag(&amp;#039;div&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-toggleable-heading&amp;#039;)&lt;br /&gt;
			:addClass(&amp;#039;druid-toggleable&amp;#039;)&lt;br /&gt;
			:attr(&amp;#039;data-druid&amp;#039;, h.counter .. &amp;#039;-&amp;#039; .. i)&lt;br /&gt;
			:wikitext(args[section .. &amp;#039;_label&amp;#039;] or section)&lt;br /&gt;
		-- we are going to print the section content even in empty nodes&lt;br /&gt;
		-- for compatibility with browsers without :has, where hiding empty rows won&amp;#039;t happen&lt;br /&gt;
		if emptySections[label] then&lt;br /&gt;
			div:addClass(&amp;#039;druid-toggleable-heading-empty&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		if i == 1 then&lt;br /&gt;
			div:addClass(&amp;#039;focused&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.printSectionTabs(node, section, args)&lt;br /&gt;
	local tabs = args[section .. &amp;#039;_tabs&amp;#039;]&lt;br /&gt;
	if not tabs or #tabs == 0 then return end&lt;br /&gt;
	local tr = node:tag(h.getTag(&amp;#039;sectionTabsOuter&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;data-druid-section&amp;#039;, h.escape(section))&lt;br /&gt;
	local th = tr:tag(h.getTag(&amp;#039;sectionTabs&amp;#039;))&lt;br /&gt;
		:attr(&amp;#039;colspan&amp;#039;, 2)&lt;br /&gt;
		:addClass(&amp;#039;druid-section-tabs&amp;#039;)&lt;br /&gt;
		:addClass(&amp;#039;druid-section-tabs-&amp;#039; .. h.escape(section))&lt;br /&gt;
	local texts = {}&lt;br /&gt;
	for i, item in ipairs(tabs) do&lt;br /&gt;
		texts[i] = args[item .. &amp;#039;_label&amp;#039;] or item&lt;br /&gt;
	end&lt;br /&gt;
	h.printTabs(th, tabs, texts, true, args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------&lt;br /&gt;
-- general utility functions&lt;br /&gt;
----------------------------&lt;br /&gt;
&lt;br /&gt;
function h.overwrite()&lt;br /&gt;
	-- this is a generic utility function that collects args from the invoke call &amp;amp; the parent template.&lt;br /&gt;
	-- normally, you merge args with parent template overwriting the invoke call, but&lt;br /&gt;
	-- since we&amp;#039;ll be putting markup/formatting into our invoke call,&lt;br /&gt;
	-- we actually want to overwrite what the user sent.&lt;br /&gt;
	local f = mw.getCurrentFrame()&lt;br /&gt;
	local origArgs = f.args&lt;br /&gt;
	local parentArgs = f:getParent().args&lt;br /&gt;
&lt;br /&gt;
	local args = {}&lt;br /&gt;
	&lt;br /&gt;
	for k, v in pairs(parentArgs) do&lt;br /&gt;
		v = mw.text.trim(v)&lt;br /&gt;
		if v ~= &amp;#039;&amp;#039; then&lt;br /&gt;
			args[k] = v&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	for k, v in pairs(origArgs) do&lt;br /&gt;
		v = mw.text.trim(tostring(v))&lt;br /&gt;
		if v ~= &amp;#039;&amp;#039; then&lt;br /&gt;
			args[k] = v&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- generic utility functions&lt;br /&gt;
-- these would normally be provided by other modules, but to make installation easy&lt;br /&gt;
-- I&amp;#039;m including everything here&lt;br /&gt;
&lt;br /&gt;
function h.split(text, pattern, plain)&lt;br /&gt;
	if not text then&lt;br /&gt;
		return {}&lt;br /&gt;
	end&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for m in h.gsplit(text, pattern, plain) do&lt;br /&gt;
		ret[#ret+1] = m&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.gsplit( text, pattern, plain )&lt;br /&gt;
	if not pattern then pattern = &amp;#039;,&amp;#039; end&lt;br /&gt;
	if not plain then&lt;br /&gt;
		pattern = &amp;#039;%s*&amp;#039; .. pattern .. &amp;#039;%s*&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local s, l = 1, text:len()&lt;br /&gt;
	return function ()&lt;br /&gt;
		if s then&lt;br /&gt;
			local e, n = text:find( pattern, s, plain )&lt;br /&gt;
			local ret&lt;br /&gt;
			if not e then&lt;br /&gt;
				ret = text:sub( s )&lt;br /&gt;
				s = nil&lt;br /&gt;
			elseif n &amp;lt; e then&lt;br /&gt;
				-- Empty separator!&lt;br /&gt;
				ret = text:sub( s, e )&lt;br /&gt;
				if e &amp;lt; l then&lt;br /&gt;
					s = e + 1&lt;br /&gt;
				else&lt;br /&gt;
					s = nil&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				ret = e &amp;gt; s and text:sub( s, e - 1 ) or &amp;#039;&amp;#039;&lt;br /&gt;
				s = n + 1&lt;br /&gt;
			end&lt;br /&gt;
			return ret&lt;br /&gt;
		end&lt;br /&gt;
	end, nil, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function h.escape(s)&lt;br /&gt;
	s = s:gsub(&amp;#039; &amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;#039;&amp;quot;&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;&amp;#039;&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;%?&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;%%&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;%[&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;%]&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;{&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;}&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;quot;!&amp;quot;, &amp;#039;&amp;#039;)&lt;br /&gt;
	return s&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- normally I would make these constants at the top of the file&lt;br /&gt;
-- but I don&amp;#039;t want to mistake them with user-set constants&lt;br /&gt;
h.boolFalse = { [&amp;#039;false&amp;#039;] = true, [&amp;#039;no&amp;#039;] = true, [&amp;#039;&amp;#039;] = true, [&amp;#039;0&amp;#039;] = true, [&amp;#039;nil&amp;#039;] = true }&lt;br /&gt;
&lt;br /&gt;
function h.castBool(x)&lt;br /&gt;
	if not x then return false end&lt;br /&gt;
	return not h.boolFalse[tostring(x):lower()]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
h.htmlEntities = {&lt;br /&gt;
	table = {&lt;br /&gt;
		container = &amp;#039;table&amp;#039;,&lt;br /&gt;
		titleOuter = &amp;#039;tr&amp;#039;,&lt;br /&gt;
		titleInner = &amp;#039;th&amp;#039;,&lt;br /&gt;
		section = &amp;#039;&amp;#039;,&lt;br /&gt;
		sectionTitle = &amp;#039;th&amp;#039;,&lt;br /&gt;
		sectionTabsOuter = &amp;#039;tr&amp;#039;,&lt;br /&gt;
		sectionTabs = &amp;#039;td&amp;#039;,&lt;br /&gt;
		row = &amp;#039;tr&amp;#039;,&lt;br /&gt;
		label = &amp;#039;th&amp;#039;,&lt;br /&gt;
		cell = &amp;#039;td&amp;#039;,&lt;br /&gt;
	},&lt;br /&gt;
	div = {&lt;br /&gt;
		container = &amp;#039;div&amp;#039;,&lt;br /&gt;
		titleOuter = &amp;#039;div&amp;#039;,&lt;br /&gt;
		titleInner = &amp;#039;div&amp;#039;,&lt;br /&gt;
		section = &amp;#039;div&amp;#039;,&lt;br /&gt;
		sectionTitle = &amp;#039;div&amp;#039;,&lt;br /&gt;
		sectionTabsOuter = &amp;#039;div&amp;#039;,&lt;br /&gt;
		sectionTabs = &amp;#039;div&amp;#039;,&lt;br /&gt;
		row = &amp;#039;div&amp;#039;,&lt;br /&gt;
		label = &amp;#039;div&amp;#039;,&lt;br /&gt;
		cell = &amp;#039;div&amp;#039;,&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function h.getTag(key)&lt;br /&gt;
	-- try not to totally fail here&lt;br /&gt;
	return h.htmlEntities[h.entityType or &amp;#039;div&amp;#039;][key]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Dreitona</name></author>
	</entry>
</feed>