<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AProbox</id>
	<title>Module:Probox - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AProbox"/>
	<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:Probox&amp;action=history"/>
	<updated>2026-04-04T14:42:58Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://the-democratika.com/wiki/index.php?title=Module:Probox&amp;diff=5915&amp;oldid=prev</id>
		<title>&gt;Uzume: Update from master using Synchronizer #synchronizer</title>
		<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:Probox&amp;diff=5915&amp;oldid=prev"/>
		<updated>2023-06-12T17:55:51Z</updated>

		<summary type="html">&lt;p&gt;Update from &lt;a href=&quot;/wiki/index.php?title=D:Special:GoToLinkedPage/metawiki/Q22137056&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;D:Special:GoToLinkedPage/metawiki/Q22137056 (page does not exist)&quot;&gt;master&lt;/a&gt; using &lt;a href=&quot;https://www.mediawiki.org/wiki/Synchronizer&quot; class=&quot;extiw&quot; title=&quot;mw:Synchronizer&quot;&gt;Synchronizer&lt;/a&gt; #synchronizer&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--&lt;br /&gt;
-- This module implements {{Probox}}&lt;br /&gt;
--&lt;br /&gt;
local getArgs = require(&amp;#039;Module:Arguments&amp;#039;).getArgs&lt;br /&gt;
local TableTools = require(&amp;#039;Module:TableTools&amp;#039;)&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
--function GetPageLang (frame)	local pagelang = frame:callParserFunction{ name = &amp;#039;#translation:&amp;#039;} 	if pagelang == nil then pagelang = &amp;quot;/en&amp;quot; end	return pagelang end&lt;br /&gt;
&lt;br /&gt;
local function stringStarts(String,Start)&lt;br /&gt;
  return string.sub(String,1,string.len(Start))==Start&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stringToLowerCase(value)&lt;br /&gt;
	return mw.ustring.lower(value)&lt;br /&gt;
end&lt;br /&gt;
 &lt;br /&gt;
local function stringSpacesToUnderscores(value)&lt;br /&gt;
	return mw.ustring.gsub(value, &amp;quot; &amp;quot;, &amp;quot;_&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stringFirstCharToUpper(str)&lt;br /&gt;
    return (str:gsub(&amp;quot;^%l&amp;quot;, string.upper))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function addTemplates(frame, data, args)&lt;br /&gt;
	-- adds additional template to the top of the page, above the infobox&lt;br /&gt;
	local page_template&lt;br /&gt;
	-- local tmplt_args = {}&lt;br /&gt;
	local template_div = mw.html.create(&amp;#039;div&amp;#039;)&lt;br /&gt;
 	for k,v in pairs(data.templates) do -- if there is a matching arg, and it has a table of template possibilities&lt;br /&gt;
 		if (args[k] and string.len(args[k]) &amp;gt; 0 and type(data.templates[k]) == &amp;quot;table&amp;quot;) then --convert args to lowercase before checking them against cats&lt;br /&gt;
 			for tmplt_key, tmplt_val in pairs(data.templates[k]) do&lt;br /&gt;
				if data.templates.passArg == true then&lt;br /&gt;
					template_div&lt;br /&gt;
						:cssText(&amp;quot;&amp;quot;)&lt;br /&gt;
						:wikitext(frame:expandTemplate{title=data.templates[k][tmplt_key], args={status = stringFirstCharToUpper(args[k])}}) --status is special casing. need to pass generic arg keys&lt;br /&gt;
						:done()&lt;br /&gt;
				break&lt;br /&gt;
				elseif (stringToLowerCase(args[k]) == tmplt_key and mw.title.new(&amp;quot;Template:&amp;quot; .. tmplt_val).exists) then &lt;br /&gt;
                        template_div&lt;br /&gt;
                            :cssText(&amp;quot;margin-bottom:1em;&amp;quot;)&lt;br /&gt;
                            :wikitext(frame:expandTemplate{title=tmplt_val, args={}})&lt;br /&gt;
                            :done()&lt;br /&gt;
                end&lt;br /&gt;
                    --convert args to lowercase and subs spaces for underscores&lt;br /&gt;
                    --make sure specified template exists&lt;br /&gt;
                    -- if (type(tmplt_val) == &amp;quot;string&amp;quot; and stringToLowerCase(args[k]) == tmplt_key and mw.title.new(&amp;quot;Template:&amp;quot; .. tmplt_val).exists) then &lt;br /&gt;
&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
 	end&lt;br /&gt;
 	page_template = tostring(template_div)&lt;br /&gt;
	return page_template&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function addCategories(frame, data, args, open_roles)&lt;br /&gt;
	-- will also look for numbered categories&lt;br /&gt;
	local cat_list = {}&lt;br /&gt;
	local base_cat = data.categories.base&lt;br /&gt;
	-- table.insert(cat_list, base_cat) --always need a base category&lt;br /&gt;
	-- -- adding role categories&lt;br /&gt;
	if data.categories.default then&lt;br /&gt;
		table.insert(cat_list, data.categories.default)&lt;br /&gt;
	end&lt;br /&gt;
	if data.categories.roles then&lt;br /&gt;
		role_cat = data.categories.roles&lt;br /&gt;
		for k,v in pairs(open_roles) do&lt;br /&gt;
			table.insert(cat_list, base_cat .. k:gsub(&amp;quot;^%l&amp;quot;, string.upper) .. role_cat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
 	for k,v in pairs(data.categories) do -- if there is a matching arg, and it has a table of category possibilities&lt;br /&gt;
 		if (args[k] and string.len(args[k]) &amp;gt; 0) then --convert args to lowercase before checking them against cats&lt;br /&gt;
 			if type(data.categories[k]) == &amp;quot;table&amp;quot; then&lt;br /&gt;
				for cat_key, cat_val in pairs(data.categories[k]) do&lt;br /&gt;
					if stringSpacesToUnderscores(stringToLowerCase(args[k])) == cat_key then --convert args to lowercase and subs spaces for underscores before checking them against cats&lt;br /&gt;
						table.insert(cat_list, base_cat .. cat_val)&lt;br /&gt;
						break&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			elseif type(data.categories[k]) == &amp;quot;string&amp;quot; then --concat the value of the cat field with the default cat directly&lt;br /&gt;
				table.insert(cat_list, base_cat .. data.categories[k])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
 	end&lt;br /&gt;
 	&lt;br /&gt;
 	local page_categories = &amp;quot;&amp;quot;&lt;br /&gt;
 	-- testing&lt;br /&gt;
	local pagelang = mw.text.trim(frame:callParserFunction{name=&amp;#039;#translation&amp;#039;, args=&amp;quot;1&amp;quot;})&lt;br /&gt;
--	local pagelang = &amp;quot;/&amp;quot; .. mw.text.trim(frame:expandTemplate{title=&amp;#039;CURRENTCONTENTLANGUAGE&amp;#039;})&lt;br /&gt;
&lt;br /&gt;
 	if #cat_list &amp;gt; 0 then&lt;br /&gt;
 		if pagelang == nil then&lt;br /&gt;
 			page_categories = &amp;quot;[[&amp;quot; .. tostring(table.concat(cat_list, &amp;quot;]] [[&amp;quot;)) .. &amp;quot;]]&amp;quot;&lt;br /&gt;
 		else&lt;br /&gt;
 			page_categories = &amp;quot;[[&amp;quot; .. tostring(table.concat(cat_list, pagelang .. &amp;quot;]] [[&amp;quot;)) .. pagelang .. &amp;quot;]]&amp;quot;&lt;br /&gt;
 		end&lt;br /&gt;
 	end&lt;br /&gt;
 	&lt;br /&gt;
 	if(args[&amp;quot;status&amp;quot;] == &amp;quot;selected&amp;quot; or args[&amp;quot;status&amp;quot;] == &amp;quot;SELECTED&amp;quot;) then&lt;br /&gt;
		maintainer = args[&amp;quot;grantee&amp;quot;] or args[&amp;quot;organization&amp;quot;] or args[&amp;quot;creator&amp;quot;] or args[&amp;quot;grantee1&amp;quot;] or args[&amp;quot;grantee2&amp;quot;]&lt;br /&gt;
		&lt;br /&gt;
		--if username is given as wikilink, extract clean username&lt;br /&gt;
		maintainer = mw.ustring.match(maintainer, &amp;quot;%[%[[Uu]ser:(.+)|&amp;quot;) or maintainer&lt;br /&gt;
		&lt;br /&gt;
		page_categories = page_categories.. &amp;quot;[[Category:WMF grant reports by grantee|&amp;quot;..maintainer..&amp;quot;]]&amp;quot;&lt;br /&gt;
		&lt;br /&gt;
		category_per_grantee = mw.title.new(&amp;quot;Category:&amp;quot;..maintainer)&lt;br /&gt;
		if category_per_grantee.exists then page_categories = page_categories.. &amp;quot;[[Category:&amp;quot;..maintainer..&amp;quot;]]&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
 	&lt;br /&gt;
	return page_categories&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeTextField(field, field_div)&lt;br /&gt;
	-- makes a formatted text field for output, based on parameter&lt;br /&gt;
	-- values or on default values provided for that field &lt;br /&gt;
	-- type in the stylesheet&lt;br /&gt;
	field_div:cssText(field.style)&lt;br /&gt;
	if field.vtype2 == &amp;quot;body&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
				:cssText(field.style2)--inconsistent use of styles 2/3 here&lt;br /&gt;
				:wikitext(field.title)--we probably aren&amp;#039;t using this for most things now, after Heather&amp;#039;s redesign&lt;br /&gt;
				:done()&lt;br /&gt;
			:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
				:cssText(field.style3)&lt;br /&gt;
				:wikitext(field.values[1])&lt;br /&gt;
				:done()&lt;br /&gt;
	elseif field.vtype2 == &amp;quot;title&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
				:cssText(field.style3)&lt;br /&gt;
				:wikitext(field.values[1])&lt;br /&gt;
				:done()&lt;br /&gt;
	elseif field.vtype2 == &amp;quot;link&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
				:cssText(field.style3)&lt;br /&gt;
				:wikitext(&amp;quot;[[&amp;quot; .. field.values[1] .. &amp;quot;|&amp;lt;span style=&amp;#039;&amp;quot; .. field.style2 .. &amp;quot;&amp;#039;&amp;gt;&amp;quot; .. field.title ..&amp;quot;&amp;lt;/span&amp;gt;]]&amp;quot;)&lt;br /&gt;
				:done()&lt;br /&gt;
	end&lt;br /&gt;
	field_div:done()&lt;br /&gt;
	return field_div&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeImageField(field, field_div)&lt;br /&gt;
	-- makes a formatted image field for output, based on parameter values&lt;br /&gt;
	-- provided in the calling template or its parent template, or on default&lt;br /&gt;
	-- values provided in the stylesheet&lt;br /&gt;
	field_div:cssText(field.style)&lt;br /&gt;
	field_div&lt;br /&gt;
		:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
		:cssText(field.style3)&lt;br /&gt;
	if field.vtype2 == &amp;quot;thumb&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:wikitext(&amp;quot;[[&amp;quot; .. field.values[1] .. &amp;quot;|right|&amp;quot;  .. field.width ..&amp;quot;]]&amp;quot;)&lt;br /&gt;
	elseif field.vtype2 == &amp;quot;thumb2&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:wikitext(&amp;quot;[[&amp;quot; .. field.values[1] .. &amp;quot;|center|&amp;quot;  .. field.width ..&amp;quot;]]&amp;quot;)&lt;br /&gt;
	elseif field.vtype2 == &amp;quot;link&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:wikitext(&amp;quot;[[&amp;quot; .. field.values[1] .. &amp;quot;|&amp;quot; .. field.alignment .. &amp;quot;|&amp;quot; .. field.width ..&amp;quot;|link=&amp;quot; .. field.link .. &amp;quot;]]&amp;quot;)&lt;br /&gt;
	elseif field.vtype2 == &amp;quot;badge&amp;quot; then&lt;br /&gt;
		if mw.ustring.find( field.values[1], &amp;quot;http&amp;quot;, 1, true ) then&lt;br /&gt;
			field_div&lt;br /&gt;
				:wikitext(&amp;quot;[[&amp;quot; .. field.icon .. &amp;quot;|&amp;quot; .. field.alignment .. &amp;quot;|&amp;quot; .. field.width ..&amp;quot;|link=&amp;quot; .. field.values[1] .. &amp;quot;]] &amp;quot; .. &amp;quot;[&amp;quot; .. field.values[1] .. &amp;quot; &amp;quot; .. field.title .. &amp;quot;]&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	elseif field.vtype2 == &amp;quot;ui_button&amp;quot; then&lt;br /&gt;
		field_div&lt;br /&gt;
			:addClass(field.class)&lt;br /&gt;
			:wikitext(field.title)&lt;br /&gt;
			:done()&lt;br /&gt;
	end&lt;br /&gt;
	field_div:done()&lt;br /&gt;
	return field_div&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeParticipantField(field, ftype)&lt;br /&gt;
	local field_div = mw.html.create(&amp;#039;div&amp;#039;)&lt;br /&gt;
	local title_span&lt;br /&gt;
	if field.icon then &lt;br /&gt;
		title_span = &amp;quot;[[&amp;quot; .. field.icon .. &amp;quot;|left&amp;quot; .. &amp;quot;|18px]] &amp;quot; .. field.title&lt;br /&gt;
	else&lt;br /&gt;
		title_span = field.title&lt;br /&gt;
	end&lt;br /&gt;
	field_div&lt;br /&gt;
		:cssText(field.style)&lt;br /&gt;
		:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
			:cssText(field.style2)&lt;br /&gt;
			:wikitext(title_span)&lt;br /&gt;
			:done()&lt;br /&gt;
	if ftype == &amp;quot;filled&amp;quot; then&lt;br /&gt;
		local i = 1&lt;br /&gt;
		for k,v in ipairs(field.values) do&lt;br /&gt;
			if (i &amp;gt; 1 and field.icon) then --only insert extra padding if has icon&lt;br /&gt;
				field.style3 = &amp;quot;padding-left:25px; display:block&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			if field.vtype2 then --ideally all configs should at least have this field for participants. FIXME&lt;br /&gt;
				if field.vtype2 == &amp;quot;username&amp;quot; then&lt;br /&gt;
					v = &amp;quot;• &amp;quot; .. &amp;quot;[[User:&amp;quot; .. v .. &amp;quot;|&amp;quot; .. v .. &amp;quot;]]&amp;quot;&lt;br /&gt;
				elseif field.vtype2 == &amp;quot;email&amp;quot; then&lt;br /&gt;
					v = &amp;quot;• &amp;quot; .. v&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				v = &amp;quot;• &amp;quot; .. &amp;quot;[[User:&amp;quot; .. v .. &amp;quot;|&amp;quot; .. v .. &amp;quot;]]&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			field_div&lt;br /&gt;
				:tag(&amp;#039;span&amp;#039;)&lt;br /&gt;
					:cssText(field.style3)&lt;br /&gt;
					:wikitext(v)&lt;br /&gt;
--					:wikitext(&amp;quot;• &amp;quot; .. &amp;quot;[[User:&amp;quot; .. v .. &amp;quot;|&amp;quot; .. v .. &amp;quot;]]&amp;quot;)&lt;br /&gt;
					:done()&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	field_div:allDone()&lt;br /&gt;
	return field_div&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeSectionDiv(sec_fields, sec_style)&lt;br /&gt;
	local sec_div = mw.html.create(&amp;#039;div&amp;#039;):cssText(sec_style)&lt;br /&gt;
	sec_fields = TableTools.compressSparseArray(sec_fields)&lt;br /&gt;
	for findex, sec_field in ipairs(sec_fields) do -- should put this at the end of the function, and just append the other stuff&lt;br /&gt;
		sec_div:node(sec_field)&lt;br /&gt;
	end&lt;br /&gt;
	return sec_div&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeParticipantsSection(frame, args, data, filled_role_data, open_roles)&lt;br /&gt;
	local filled_role_fields = {}&lt;br /&gt;
	for role, val_table in pairs(filled_role_data) do&lt;br /&gt;
		local field = data.fields[role]&lt;br /&gt;
		field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})&lt;br /&gt;
		field.values = {}&lt;br /&gt;
		for val_num, val_text in ipairs(filled_role_data[role]) do&lt;br /&gt;
			field.values[#field.values + 1] = val_text&lt;br /&gt;
		end&lt;br /&gt;
		local filled_field_div = makeParticipantField(field, &amp;quot;filled&amp;quot;)&lt;br /&gt;
        filled_role_fields[field.rank] = filled_field_div&lt;br /&gt;
	end&lt;br /&gt;
	local sec_div = makeSectionDiv(filled_role_fields, data.styles.section[&amp;quot;participants&amp;quot;]) &lt;br /&gt;
	if (data.fields.more_participants and args.more_participants and stringToLowerCase(args.more_participants)) == &amp;quot;yes&amp;quot; then -- really need this here?&lt;br /&gt;
		-- if (args.portal == &amp;quot;Idealab&amp;quot; or args.portal == &amp;quot;Research&amp;quot;) then -- beware, exceptions everywhere&lt;br /&gt;
		sec_div:tag(&amp;#039;span&amp;#039;):cssText(&amp;quot;font-style:italic; color: #888888&amp;quot;):wikitext(mw.text.trim(frame:expandTemplate{title=args.translations, args={data.fields.more_participants.key}})):done()&lt;br /&gt;
--		elseif args.portal == &amp;quot;Patterns&amp;quot; then&lt;br /&gt;
--			sec_div:tag(&amp;#039;span&amp;#039;):cssText(&amp;quot;font-style:italic; color: #888888&amp;quot;):wikitext(&amp;quot;a learning pattern for...&amp;quot;):done()&lt;br /&gt;
--		else&lt;br /&gt;
		for role, val in pairs(open_roles) do -- should make these ordered using compressSparseArray, as above&lt;br /&gt;
			local field = data.fields[role]&lt;br /&gt;
			field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})&lt;br /&gt;
			if field.icon then&lt;br /&gt;
				field.icon = field.icon_inactive&lt;br /&gt;
			end&lt;br /&gt;
			local open_field_div = makeParticipantField(field, &amp;quot;open&amp;quot;)&lt;br /&gt;
			sec_div:node(open_field_div)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	sec_div:allDone()&lt;br /&gt;
	return sec_div&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeSectionFields(args, field)&lt;br /&gt;
	-- ui button is separate&lt;br /&gt;
	local field_div = mw.html.create(&amp;#039;div&amp;#039;):cssText(field.style) --why declare this here?&lt;br /&gt;
	if field.vtype == &amp;quot;image&amp;quot; then&lt;br /&gt;
		if (field.isRequired == true or (args[field.arg] and string.len(args[field.arg]) &amp;gt; 0))  then --should move this up, may not just apply to images&lt;br /&gt;
			field_div = makeImageField(field, field_div)&lt;br /&gt;
		end&lt;br /&gt;
	elseif field.vtype == &amp;quot;text&amp;quot; then&lt;br /&gt;
		field_div = makeTextField(field, field_div)&lt;br /&gt;
	else&lt;br /&gt;
	end&lt;br /&gt;
	return field_div -- make sure div is &amp;#039;done&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeSection(frame, args, data, box_sec) &lt;br /&gt;
	-- return a div for a section of the box including child divs&lt;br /&gt;
	-- for each content field in that section&lt;br /&gt;
	-- local sec_div = mw.html.create(&amp;#039;div&amp;#039;):cssText(data.styles.section[box_sec])&lt;br /&gt;
	local sec_fields = {}&lt;br /&gt;
	for k,v in pairs(data.fields) do&lt;br /&gt;
		if data.fields[k].section == box_sec then&lt;br /&gt;
			local field = data.fields[k]&lt;br /&gt;
			field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})&lt;br /&gt;
			field.values = {}&lt;br /&gt;
			if (args[k] and string.len(args[k]) &amp;gt; 0) then&lt;br /&gt;
				field.values[1] = args[k] --does not accept numbered args&lt;br /&gt;
				if field.toLowerCase == true then -- special casing to make IEG status=SELECTED to display in lowercase&lt;br /&gt;
					field.values[1] = stringToLowerCase(field.values[1])&lt;br /&gt;
				end&lt;br /&gt;
				local field_div = makeSectionFields(args, field)&lt;br /&gt;
				sec_fields[field.rank] = field_div&lt;br /&gt;
			elseif field.isRequired == true then&lt;br /&gt;
				if field.vtype == &amp;quot;text&amp;quot; then&lt;br /&gt;
					field.values[1] = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.default}})&lt;br /&gt;
				else&lt;br /&gt;
					field.values[1] = field.default&lt;br /&gt;
				end&lt;br /&gt;
				local field_div = makeSectionFields(args, field)&lt;br /&gt;
				sec_fields[field.rank] = field_div&lt;br /&gt;
			else&lt;br /&gt;
				--don&amp;#039;t make a section for this field&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local sec_div = makeSectionDiv(sec_fields, data.styles.section[box_sec]) &lt;br /&gt;
	return sec_div&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeInfobox(frame, args, data, filled_role_data, open_roles)&lt;br /&gt;
	-- builds the infobox. Some content sections are required, others &lt;br /&gt;
	-- are optional. Optional sections are defined in the stylesheet.&lt;br /&gt;
	local box = mw.html.create(&amp;#039;div&amp;#039;):cssText(data.styles.box.outer)&lt;br /&gt;
	local inner_box = mw.html.create(&amp;#039;div&amp;#039;):cssText(data.styles.box.inner)&lt;br /&gt;
	if data.sections.above == true then&lt;br /&gt;
		local sec_top = makeSection(frame, args, data, &amp;quot;above&amp;quot;)&lt;br /&gt;
		box:node(sec_top)&lt;br /&gt;
	end&lt;br /&gt;
	if data.sections.nav == true then&lt;br /&gt;
		local sec_nav = makeSection(frame, args, data, &amp;quot;nav&amp;quot;)&lt;br /&gt;
		box:node(sec_nav)&lt;br /&gt;
	end&lt;br /&gt;
	local sec_head = makeSection(frame, args, data, &amp;quot;head&amp;quot;)&lt;br /&gt;
	inner_box:node(sec_head)&lt;br /&gt;
	local sec_main = makeSection(frame, args, data, &amp;quot;main&amp;quot;)&lt;br /&gt;
	inner_box:node(sec_main)&lt;br /&gt;
	if data.sections.participants == true then&lt;br /&gt;
		local sec_participants = makeParticipantsSection(frame, args, data, filled_role_data, open_roles)&lt;br /&gt;
		inner_box:node(sec_participants)&lt;br /&gt;
	end&lt;br /&gt;
	if data.sections.cta == true then&lt;br /&gt;
		local sec_cta = makeSection(frame, args, data, &amp;quot;cta&amp;quot;)&lt;br /&gt;
		sec_cta:addClass(&amp;quot;noprint&amp;quot;) -- no much use in print outs&lt;br /&gt;
		inner_box:node(sec_cta)&lt;br /&gt;
		inner_box:tag(&amp;#039;div&amp;#039;):cssText(&amp;quot;clear:both;&amp;quot;):done() --clears buttons in the cta sections&lt;br /&gt;
	end&lt;br /&gt;
	inner_box:allDone()&lt;br /&gt;
	box:node(inner_box)&lt;br /&gt;
	if data.sections.below == true then&lt;br /&gt;
		local sec_bottom = makeSection(frame, args, data, &amp;quot;below&amp;quot;)&lt;br /&gt;
		box:node(sec_bottom)&lt;br /&gt;
	end&lt;br /&gt;
	box:allDone()&lt;br /&gt;
	return box&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function orderStringtoNumber(array, val, num)&lt;br /&gt;
    if num &amp;gt; table.getn(array) then&lt;br /&gt;
        array[#array+1] = val&lt;br /&gt;
    else&lt;br /&gt;
        table.insert(array, num, val)&lt;br /&gt;
    end&lt;br /&gt;
    return array&lt;br /&gt;
end&lt;br /&gt;
    &lt;br /&gt;
local function isJoinable(args, data)&lt;br /&gt;
	if args.more_participants == &amp;quot;NO&amp;quot; then&lt;br /&gt;
		data.fields.join = nil&lt;br /&gt;
		if data.fields.endorse then&lt;br /&gt;
			data.fields.endorse.style = &amp;quot;display:inline; float:right;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return data&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function deepCopyTable(data)&lt;br /&gt;
	-- the deep copy is a workaround step to avoid the restrictions placed on &lt;br /&gt;
	-- tables imported through loadData&lt;br /&gt;
	if type(data) ~= &amp;#039;table&amp;#039; then return data end&lt;br /&gt;
	local res = {}&lt;br /&gt;
	for k,v in pairs(data) do&lt;br /&gt;
		if type(v) == &amp;#039;table&amp;#039; then&lt;br /&gt;
		v = deepCopyTable(v)&lt;br /&gt;
		end&lt;br /&gt;
		res[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return res&lt;br /&gt;
end&lt;br /&gt;
	&lt;br /&gt;
local function getPortalData(args)&lt;br /&gt;
	-- loads the relevant stylesheet, if a sub-template was called with a portal&lt;br /&gt;
	-- argument and a stylesheet exists with the same name. For example, calling &lt;br /&gt;
	-- {{#invoke:Probox|main|portal=Idealab}} would load the Module:Probox/Idealab&lt;br /&gt;
	-- stylesheet.&lt;br /&gt;
	local data_readOnly = {}&lt;br /&gt;
	local data_writable = {}&lt;br /&gt;
	if (args.portal and mw.title.makeTitle( &amp;#039;Module&amp;#039;, &amp;#039;Probox/&amp;#039; .. args.portal).exists) then&lt;br /&gt;
		data_readOnly = mw.loadData(&amp;quot;Module:Probox/&amp;quot; .. args.portal)&lt;br /&gt;
	else&lt;br /&gt;
		data_readOnly = mw.loadData(&amp;quot;Module:Probox/Default&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	-- data_writable = TableTools.shallowClone(data_readOnly)&lt;br /&gt;
	data_writable = deepCopyTable(data_readOnly)&lt;br /&gt;
	return data_writable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function TCTlookup(args)&lt;br /&gt;
	local tct_path = tostring(args.translations)&lt;br /&gt;
	 --mw.log(mw.title.getCurrentTitle().subpageText)&lt;br /&gt;
	 local tct_subpage = mw.title.getCurrentTitle().subpageText&lt;br /&gt;
	if tct_subpage == &amp;quot;en&amp;quot; then&lt;br /&gt;
		tct_path = tct_path .. &amp;quot;/&amp;quot; .. tct_subpage&lt;br /&gt;
	elseif (mw.title.new(&amp;quot;Template:&amp;quot; .. args.translations .. &amp;quot;/&amp;quot; .. tct_subpage).exists and mw.language.isSupportedLanguage(tct_subpage)) then&lt;br /&gt;
		tct_path = tct_path .. &amp;quot;/&amp;quot; .. tct_subpage&lt;br /&gt;
	else&lt;br /&gt;
		tct_path = tct_path .. &amp;quot;/&amp;quot; .. &amp;quot;en&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	-- mw.log(tct_path)&lt;br /&gt;
	return tct_path&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getRoleArgs(args, available_roles)&lt;br /&gt;
    -- returns:&lt;br /&gt;
    -- 1) a table of ordered values for valid role params, &lt;br /&gt;
    -- even if numbered nonsequentially&lt;br /&gt;
    -- 2) a table of all roles with at least 1 empty param, &lt;br /&gt;
    -- plus the default volunteer role&lt;br /&gt;
    local filled_role_data = {}&lt;br /&gt;
    local open_roles = {}&lt;br /&gt;
    if available_roles.default then -- some boxes have default role to join&lt;br /&gt;
    	open_roles[available_roles.default] = true&lt;br /&gt;
    end&lt;br /&gt;
	for rd_key, rd_val in pairs(available_roles) do&lt;br /&gt;
		for a_key, a_val in pairs(args) do&lt;br /&gt;
    		if stringStarts(a_key, rd_key) then&lt;br /&gt;
                if string.len(a_val) == 0 then&lt;br /&gt;
                    open_roles[rd_key] = true&lt;br /&gt;
                else   &lt;br /&gt;
                    if not filled_role_data[rd_key] then filled_role_data[rd_key] = {} end&lt;br /&gt;
                    local arg_num = tonumber(a_key:match(&amp;#039;^&amp;#039; .. rd_key .. &amp;#039;([1-9]%d*)$&amp;#039;))&lt;br /&gt;
                    if arg_num then&lt;br /&gt;
                        filled_role_data[rd_key] = orderStringtoNumber(filled_role_data[rd_key], a_val, arg_num)&lt;br /&gt;
                    else&lt;br /&gt;
                        table.insert(filled_role_data[rd_key], 1, a_val)&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
            end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return filled_role_data, open_roles&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local args = getArgs(frame, {removeBlanks = false})&lt;br /&gt;
	local data = getPortalData(args)&lt;br /&gt;
 	data = isJoinable(args, data)&lt;br /&gt;
	if not (args.translations and mw.title.new(&amp;quot;Template:&amp;quot; .. args.translations).exists) then&lt;br /&gt;
 		args.translations = &amp;quot;Probox/Default/Content&amp;quot;&lt;br /&gt;
 	end&lt;br /&gt;
 	-- mw.log(args.translations)&lt;br /&gt;
 	-- if the TCT content index is under translation, check for translations in the subpage language&lt;br /&gt;
 	if mw.title.new(&amp;quot;Template:&amp;quot; .. args.translations .. &amp;quot;/en&amp;quot;).exists then&lt;br /&gt;
 		args.translations = TCTlookup(args)&lt;br /&gt;
 	end&lt;br /&gt;
 	if data.sections.cta == true then&lt;br /&gt;
 		args.talk = tostring(mw.title.getCurrentTitle().talkPageTitle)  -- expensive&lt;br /&gt;
 	end&lt;br /&gt;
 	local filled_role_data, open_roles = getRoleArgs(args, data.roles)&lt;br /&gt;
	local box = makeInfobox(frame, args, data, filled_role_data, open_roles)&lt;br /&gt;
	local infobox = tostring(box)&lt;br /&gt;
	-- only add cats if not in Template or User or Meta namespace&lt;br /&gt;
	if (data.categories and (mw.title.getCurrentTitle().nsText ~= &amp;quot;Template&amp;quot; and mw.title.getCurrentTitle().nsText ~= &amp;quot;User&amp;quot; and mw.title.getCurrentTitle().nsText ~= &amp;quot;Meta&amp;quot;) and not args.noindex) then&lt;br /&gt;
		-- FIXME specify namespace in config, so that categories only appear if template is translcuded in that namespace&lt;br /&gt;
		local page_categories = addCategories(frame, data, args, open_roles)&lt;br /&gt;
		infobox = infobox .. page_categories&lt;br /&gt;
	end&lt;br /&gt;
	if data.templates then&lt;br /&gt;
		local top_template = addTemplates(frame, data, args)&lt;br /&gt;
		infobox = top_template .. infobox&lt;br /&gt;
	end&lt;br /&gt;
	return infobox&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>&gt;Uzume</name></author>
	</entry>
</feed>