From Wikipedia, the free encyclopedia


--

-- This module implements {{Probox}}

--

local getArgs = require('Module:Arguments').getArgs

local TableTools = require('Module:TableTools')

local p = {}



--function GetPageLang (frame)	local pagelang = frame:callParserFunction{ name = '#translation:'} 	if pagelang == nil then pagelang = "/en" end	return pagelang end



local function stringStarts(String,Start)

  return string.sub(String,1,string.len(Start))==Start

end



local function stringToLowerCase(value)

	return mw.ustring.lower(value)

end

 

local function stringSpacesToUnderscores(value)

	return mw.ustring.gsub(value, " ", "_")

end



local function stringFirstCharToUpper(str)

    return (str:gsub("^%l", string.upper))

end



local function addTemplates(frame, data, args)

	-- adds additional template to the top of the page, above the infobox

	local page_template

	-- local tmplt_args = {}

	local template_div = mw.html.create('div')

 	for k,v in pairs(data.templates) do -- if there is a matching arg, and it has a table of template possibilities

 		if (argsk and string.len(argsk]) > 0 and type(data.templatesk]) == "table") then --convert args to lowercase before checking them against cats

 			for tmplt_key, tmplt_val in pairs(data.templatesk]) do

				if data.templates.passArg == true then

					template_div

						:cssText("")

						:wikitext(frame:expandTemplate{title=data.templatesk][tmplt_key], args={status = stringFirstCharToUpper(argsk])}}) --status is special casing. need to pass generic arg keys

						:done()

				break

				elseif (stringToLowerCase(argsk]) == tmplt_key and mw.title.new("Template:" .. tmplt_val).exists) then 

                        template_div

                            :cssText("margin-bottom:1em;")

                            :wikitext(frame:expandTemplate{title=tmplt_val, args={}})

                            :done()

                end

                    --convert args to lowercase and subs spaces for underscores

                    --make sure specified template exists

                    -- if (type(tmplt_val) == "string" and stringToLowerCase(args[k]) == tmplt_key and mw.title.new("Template:" .. tmplt_val).exists) then 



			end

		end

 	end

 	page_template = tostring(template_div)

	return page_template

end



local function addCategories(frame, data, args, open_roles)

	-- will also look for numbered categories

	local cat_list = {}

	local base_cat = data.categories.base

	-- table.insert(cat_list, base_cat) --always need a base category

	-- -- adding role categories

	if data.categories.default then

		table.insert(cat_list, data.categories.default)

	end

	if data.categories.roles then

		role_cat = data.categories.roles

		for k,v in pairs(open_roles) do

			table.insert(cat_list, base_cat .. k:gsub("^%l", string.upper) .. role_cat)

		end

	end

 	for k,v in pairs(data.categories) do -- if there is a matching arg, and it has a table of category possibilities

 		if (argsk and string.len(argsk]) > 0) then --convert args to lowercase before checking them against cats

 			if type(data.categoriesk]) == "table" then

				for cat_key, cat_val in pairs(data.categoriesk]) do

					if stringSpacesToUnderscores(stringToLowerCase(argsk])) == cat_key then --convert args to lowercase and subs spaces for underscores before checking them against cats

						table.insert(cat_list, base_cat .. cat_val)

						break

					end

				end

			elseif type(data.categoriesk]) == "string" then --concat the value of the cat field with the default cat directly

				table.insert(cat_list, base_cat .. data.categoriesk])

			end

		end

 	end

 	

 	local page_categories = ""

 	-- testing

	local pagelang = mw.text.trim(frame:callParserFunction{name='#translation', args="1"})

--	local pagelang = "/" .. mw.text.trim(frame:expandTemplate{title='CURRENTCONTENTLANGUAGE'})



 	if #cat_list > 0 then

 		if pagelang == nil then

 			page_categories = "[[" .. tostring(table.concat(cat_list, "]] [[")) .. "]]"

 		else

 			page_categories = "[[" .. tostring(table.concat(cat_list, pagelang .. "]] [[")) .. pagelang .. "]]"

 		end

 	end

 	

 	if(args"status" == "selected" or args"status" == "SELECTED") then

		maintainer = args"grantee" or args"organization" or args"creator" or args"grantee1" or args"grantee2"

		

		--if username is given as wikilink, extract clean username

		maintainer = mw.ustring.match(maintainer, "%[%[[Uu]ser:(.+)|") or maintainer

		

		page_categories = page_categories.. "[[Category:WMF grant reports by grantee|"..maintainer.."]]"

		

		category_per_grantee = mw.title.new("Category:"..maintainer)

		if category_per_grantee.exists then page_categories = page_categories.. "[[Category:"..maintainer.."]]" end

	end

 	

	return page_categories

	

end



local function makeTextField(field, field_div)

	-- makes a formatted text field for output, based on parameter

	-- values or on default values provided for that field 

	-- type in the stylesheet

	field_div:cssText(field.style)

	if field.vtype2 == "body" then

		field_div

			:tag('span')

				:cssText(field.style2)--inconsistent use of styles 2/3 here

				:wikitext(field.title)--we probably aren't using this for most things now, after Heather's redesign

				:done()

			:tag('span')

				:cssText(field.style3)

				:wikitext(field.values1])

				:done()

	elseif field.vtype2 == "title" then

		field_div

			:tag('span')

				:cssText(field.style3)

				:wikitext(field.values1])

				:done()

	elseif field.vtype2 == "link" then

		field_div

			:tag('span')

				:cssText(field.style3)

				:wikitext("[[" .. field.values1 .. "|<span style='" .. field.style2 .. "'>" .. field.title .."</span>]]")

				:done()

	end

	field_div:done()

	return field_div

end



local function makeImageField(field, field_div)

	-- makes a formatted image field for output, based on parameter values

	-- provided in the calling template or its parent template, or on default

	-- values provided in the stylesheet

	field_div:cssText(field.style)

	field_div

		:tag('span')

		:cssText(field.style3)

	if field.vtype2 == "thumb" then

		field_div

			:wikitext("[[" .. field.values1 .. "|right|"  .. field.width .."]]")

	elseif field.vtype2 == "thumb2" then

		field_div

			:wikitext("[[" .. field.values1 .. "|center|"  .. field.width .."]]")

	elseif field.vtype2 == "link" then

		field_div

			:wikitext("[[" .. field.values1 .. "|" .. field.alignment .. "|" .. field.width .."|link=" .. field.link .. "]]")

	elseif field.vtype2 == "badge" then

		if mw.ustring.find( field.values1], "http", 1, true ) then

			field_div

				:wikitext("[[" .. field.icon .. "|" .. field.alignment .. "|" .. field.width .."|link=" .. field.values1 .. "]] " .. "[" .. field.values1 .. " " .. field.title .. "]")

		end

	elseif field.vtype2 == "ui_button" then

		field_div

			:addClass(field.class)

			:wikitext(field.title)

			:done()

	end

	field_div:done()

	return field_div

end



local function makeParticipantField(field, ftype)

	local field_div = mw.html.create('div')

	local title_span

	if field.icon then 

		title_span = "[[" .. field.icon .. "|left" .. "|18px]] " .. field.title

	else

		title_span = field.title

	end

	field_div

		:cssText(field.style)

		:tag('span')

			:cssText(field.style2)

			:wikitext(title_span)

			:done()

	if ftype == "filled" then

		local i = 1

		for k,v in ipairs(field.values) do

			if (i > 1 and field.icon) then --only insert extra padding if has icon

				field.style3 = "padding-left:25px; display:block"

			end

			if field.vtype2 then --ideally all configs should at least have this field for participants. FIXME

				if field.vtype2 == "username" then

					v = "• " .. "[[User:" .. v .. "|" .. v .. "]]"

				elseif field.vtype2 == "email" then

					v = "• " .. v

				end

			else

				v = "• " .. "[[User:" .. v .. "|" .. v .. "]]"

			end

			field_div

				:tag('span')

					:cssText(field.style3)

					:wikitext(v)

--					:wikitext("• " .. "[[User:" .. v .. "|" .. v .. "]]")

					:done()

			i = i + 1

		end

	end

	field_div:allDone()

	return field_div

end



local function makeSectionDiv(sec_fields, sec_style)

	local sec_div = mw.html.create('div'):cssText(sec_style)

	sec_fields = TableTools.compressSparseArray(sec_fields)

	for findex, sec_field in ipairs(sec_fields) do -- should put this at the end of the function, and just append the other stuff

		sec_div:node(sec_field)

	end

	return sec_div

end



local function makeParticipantsSection(frame, args, data, filled_role_data, open_roles)

	local filled_role_fields = {}

	for role, val_table in pairs(filled_role_data) do

		local field = data.fieldsrole

		field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})

		field.values = {}

		for val_num, val_text in ipairs(filled_role_datarole]) do

			field.values#field.values + 1 = val_text

		end

		local filled_field_div = makeParticipantField(field, "filled")

        filled_role_fieldsfield.rank = filled_field_div

	end

	local sec_div = makeSectionDiv(filled_role_fields, data.styles.section"participants"]) 

	if (data.fields.more_participants and args.more_participants and stringToLowerCase(args.more_participants)) == "yes" then -- really need this here?

		-- if (args.portal == "Idealab" or args.portal == "Research") then -- beware, exceptions everywhere

		sec_div:tag('span'):cssText("font-style:italic; color: #888888"):wikitext(mw.text.trim(frame:expandTemplate{title=args.translations, args={data.fields.more_participants.key}})):done()

--		elseif args.portal == "Patterns" then

--			sec_div:tag('span'):cssText("font-style:italic; color: #888888"):wikitext("a learning pattern for..."):done()

--		else

		for role, val in pairs(open_roles) do -- should make these ordered using compressSparseArray, as above

			local field = data.fieldsrole

			field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})

			if field.icon then

				field.icon = field.icon_inactive

			end

			local open_field_div = makeParticipantField(field, "open")

			sec_div:node(open_field_div)

		end

	end

	sec_div:allDone()

	return sec_div

end



local function makeSectionFields(args, field)

	-- ui button is separate

	local field_div = mw.html.create('div'):cssText(field.style) --why declare this here?

	if field.vtype == "image" then

		if (field.isRequired == true or (argsfield.arg and string.len(argsfield.arg]) > 0))  then --should move this up, may not just apply to images

			field_div = makeImageField(field, field_div)

		end

	elseif field.vtype == "text" then

		field_div = makeTextField(field, field_div)

	else

	end

	return field_div -- make sure div is 'done'

end



local function makeSection(frame, args, data, box_sec) 

	-- return a div for a section of the box including child divs

	-- for each content field in that section

	-- local sec_div = mw.html.create('div'):cssText(data.styles.section[box_sec])

	local sec_fields = {}

	for k,v in pairs(data.fields) do

		if data.fieldsk].section == box_sec then

			local field = data.fieldsk

			field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})

			field.values = {}

			if (argsk and string.len(argsk]) > 0) then

				field.values1 = argsk --does not accept numbered args

				if field.toLowerCase == true then -- special casing to make IEG status=SELECTED to display in lowercase

					field.values1 = stringToLowerCase(field.values1])

				end

				local field_div = makeSectionFields(args, field)

				sec_fieldsfield.rank = field_div

			elseif field.isRequired == true then

				if field.vtype == "text" then

					field.values1 = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.default}})

				else

					field.values1 = field.default

				end

				local field_div = makeSectionFields(args, field)

				sec_fieldsfield.rank = field_div

			else

				--don't make a section for this field

			end

		end

	end

	local sec_div = makeSectionDiv(sec_fields, data.styles.sectionbox_sec]) 

	return sec_div

end



local function makeInfobox(frame, args, data, filled_role_data, open_roles)

	-- builds the infobox. Some content sections are required, others 

	-- are optional. Optional sections are defined in the stylesheet.

	local box = mw.html.create('div'):cssText(data.styles.box.outer)

	local inner_box = mw.html.create('div'):cssText(data.styles.box.inner)

	if data.sections.above == true then

		local sec_top = makeSection(frame, args, data, "above")

		box:node(sec_top)

	end

	if data.sections.nav == true then

		local sec_nav = makeSection(frame, args, data, "nav")

		box:node(sec_nav)

	end

	local sec_head = makeSection(frame, args, data, "head")

	inner_box:node(sec_head)

	local sec_main = makeSection(frame, args, data, "main")

	inner_box:node(sec_main)

	if data.sections.participants == true then

		local sec_participants = makeParticipantsSection(frame, args, data, filled_role_data, open_roles)

		inner_box:node(sec_participants)

	end

	if data.sections.cta == true then

		local sec_cta = makeSection(frame, args, data, "cta")

		sec_cta:addClass("noprint") -- no much use in print outs

		inner_box:node(sec_cta)

		inner_box:tag('div'):cssText("clear:both;"):done() --clears buttons in the cta sections

	end

	inner_box:allDone()

	box:node(inner_box)

	if data.sections.below == true then

		local sec_bottom = makeSection(frame, args, data, "below")

		box:node(sec_bottom)

	end

	box:allDone()

	return box

end



local function orderStringtoNumber(array, val, num)

    if num > table.getn(array) then

        array#array+1 = val

    else

        table.insert(array, num, val)

    end

    return array

end

    

local function isJoinable(args, data)

	if args.more_participants == "NO" then

		data.fields.join = nil

		if data.fields.endorse then

			data.fields.endorse.style = "display:inline; float:right;"

		end

	end

	return data

end



local function deepCopyTable(data)

	-- the deep copy is a workaround step to avoid the restrictions placed on 

	-- tables imported through loadData

	if type(data) ~= 'table' then return data end

	local res = {}

	for k,v in pairs(data) do

		if type(v) == 'table' then

		v = deepCopyTable(v)

		end

		resk = v

	end

	return res

end

	

local function getPortalData(args)

	-- loads the relevant stylesheet, if a sub-template was called with a portal

	-- argument and a stylesheet exists with the same name. For example, calling 

	-- {{#invoke:Probox|main|portal=Idealab}} would load the Module:Probox/Idealab

	-- stylesheet.

	local data_readOnly = {}

	local data_writable = {}

	if (args.portal and mw.title.makeTitle( 'Module', 'Probox/' .. args.portal).exists) then

		data_readOnly = mw.loadData("Module:Probox/" .. args.portal)

	else

		data_readOnly = mw.loadData("Module:Probox/Default")

	end

	-- data_writable = TableTools.shallowClone(data_readOnly)

	data_writable = deepCopyTable(data_readOnly)

	return data_writable

end



local function TCTlookup(args)

	local tct_path = tostring(args.translations)

	 --mw.log(mw.title.getCurrentTitle().subpageText)

	 local tct_subpage = mw.title.getCurrentTitle().subpageText

	if tct_subpage == "en" then

		tct_path = tct_path .. "/" .. tct_subpage

	elseif (mw.title.new("Template:" .. args.translations .. "/" .. tct_subpage).exists and mw.language.isSupportedLanguage(tct_subpage)) then

		tct_path = tct_path .. "/" .. tct_subpage

	else

		tct_path = tct_path .. "/" .. "en"

	end

	-- mw.log(tct_path)

	return tct_path

end



local function getRoleArgs(args, available_roles)

    -- returns:

    -- 1) a table of ordered values for valid role params, 

    -- even if numbered nonsequentially

    -- 2) a table of all roles with at least 1 empty param, 

    -- plus the default volunteer role

    local filled_role_data = {}

    local open_roles = {}

    if available_roles.default then -- some boxes have default role to join

    	open_rolesavailable_roles.default = true

    end

	for rd_key, rd_val in pairs(available_roles) do

		for a_key, a_val in pairs(args) do

    		if stringStarts(a_key, rd_key) then

                if string.len(a_val) == 0 then

                    open_rolesrd_key = true

                else   

                    if not filled_role_datard_key then filled_role_datard_key = {} end

                    local arg_num = tonumber(a_key:match('^' .. rd_key .. '([1-9]%d*)$'))

                    if arg_num then

                        filled_role_datard_key = orderStringtoNumber(filled_role_datard_key], a_val, arg_num)

                    else

                        table.insert(filled_role_datard_key], 1, a_val)

                    end

                end

            end

		end

	end

	return filled_role_data, open_roles

end



function p.main(frame)

	local args = getArgs(frame, {removeBlanks = false})

	local data = getPortalData(args)

 	data = isJoinable(args, data)

	if not (args.translations and mw.title.new("Template:" .. args.translations).exists) then

 		args.translations = "Probox/Default/Content"

 	end

 	-- mw.log(args.translations)

 	-- if the TCT content index is under translation, check for translations in the subpage language

 	if mw.title.new("Template:" .. args.translations .. "/en").exists then

 		args.translations = TCTlookup(args)

 	end

 	if data.sections.cta == true then

 		args.talk = tostring(mw.title.getCurrentTitle().talkPageTitle)  -- expensive

 	end

 	local filled_role_data, open_roles = getRoleArgs(args, data.roles)

	local box = makeInfobox(frame, args, data, filled_role_data, open_roles)

	local infobox = tostring(box)

	-- only add cats if not in Template or User or Meta namespace

	if (data.categories and (mw.title.getCurrentTitle().nsText ~= "Template" and mw.title.getCurrentTitle().nsText ~= "User" and mw.title.getCurrentTitle().nsText ~= "Meta") and not args.noindex) then

		-- FIXME specify namespace in config, so that categories only appear if template is translcuded in that namespace

		local page_categories = addCategories(frame, data, args, open_roles)

		infobox = infobox .. page_categories

	end

	if data.templates then

		local top_template = addTemplates(frame, data, args)

		infobox = top_template .. infobox

	end

	return infobox

end



return p
From Wikipedia, the free encyclopedia


--

-- This module implements {{Probox}}

--

local getArgs = require('Module:Arguments').getArgs

local TableTools = require('Module:TableTools')

local p = {}



--function GetPageLang (frame)	local pagelang = frame:callParserFunction{ name = '#translation:'} 	if pagelang == nil then pagelang = "/en" end	return pagelang end



local function stringStarts(String,Start)

  return string.sub(String,1,string.len(Start))==Start

end



local function stringToLowerCase(value)

	return mw.ustring.lower(value)

end

 

local function stringSpacesToUnderscores(value)

	return mw.ustring.gsub(value, " ", "_")

end



local function stringFirstCharToUpper(str)

    return (str:gsub("^%l", string.upper))

end



local function addTemplates(frame, data, args)

	-- adds additional template to the top of the page, above the infobox

	local page_template

	-- local tmplt_args = {}

	local template_div = mw.html.create('div')

 	for k,v in pairs(data.templates) do -- if there is a matching arg, and it has a table of template possibilities

 		if (argsk and string.len(argsk]) > 0 and type(data.templatesk]) == "table") then --convert args to lowercase before checking them against cats

 			for tmplt_key, tmplt_val in pairs(data.templatesk]) do

				if data.templates.passArg == true then

					template_div

						:cssText("")

						:wikitext(frame:expandTemplate{title=data.templatesk][tmplt_key], args={status = stringFirstCharToUpper(argsk])}}) --status is special casing. need to pass generic arg keys

						:done()

				break

				elseif (stringToLowerCase(argsk]) == tmplt_key and mw.title.new("Template:" .. tmplt_val).exists) then 

                        template_div

                            :cssText("margin-bottom:1em;")

                            :wikitext(frame:expandTemplate{title=tmplt_val, args={}})

                            :done()

                end

                    --convert args to lowercase and subs spaces for underscores

                    --make sure specified template exists

                    -- if (type(tmplt_val) == "string" and stringToLowerCase(args[k]) == tmplt_key and mw.title.new("Template:" .. tmplt_val).exists) then 



			end

		end

 	end

 	page_template = tostring(template_div)

	return page_template

end



local function addCategories(frame, data, args, open_roles)

	-- will also look for numbered categories

	local cat_list = {}

	local base_cat = data.categories.base

	-- table.insert(cat_list, base_cat) --always need a base category

	-- -- adding role categories

	if data.categories.default then

		table.insert(cat_list, data.categories.default)

	end

	if data.categories.roles then

		role_cat = data.categories.roles

		for k,v in pairs(open_roles) do

			table.insert(cat_list, base_cat .. k:gsub("^%l", string.upper) .. role_cat)

		end

	end

 	for k,v in pairs(data.categories) do -- if there is a matching arg, and it has a table of category possibilities

 		if (argsk and string.len(argsk]) > 0) then --convert args to lowercase before checking them against cats

 			if type(data.categoriesk]) == "table" then

				for cat_key, cat_val in pairs(data.categoriesk]) do

					if stringSpacesToUnderscores(stringToLowerCase(argsk])) == cat_key then --convert args to lowercase and subs spaces for underscores before checking them against cats

						table.insert(cat_list, base_cat .. cat_val)

						break

					end

				end

			elseif type(data.categoriesk]) == "string" then --concat the value of the cat field with the default cat directly

				table.insert(cat_list, base_cat .. data.categoriesk])

			end

		end

 	end

 	

 	local page_categories = ""

 	-- testing

	local pagelang = mw.text.trim(frame:callParserFunction{name='#translation', args="1"})

--	local pagelang = "/" .. mw.text.trim(frame:expandTemplate{title='CURRENTCONTENTLANGUAGE'})



 	if #cat_list > 0 then

 		if pagelang == nil then

 			page_categories = "[[" .. tostring(table.concat(cat_list, "]] [[")) .. "]]"

 		else

 			page_categories = "[[" .. tostring(table.concat(cat_list, pagelang .. "]] [[")) .. pagelang .. "]]"

 		end

 	end

 	

 	if(args"status" == "selected" or args"status" == "SELECTED") then

		maintainer = args"grantee" or args"organization" or args"creator" or args"grantee1" or args"grantee2"

		

		--if username is given as wikilink, extract clean username

		maintainer = mw.ustring.match(maintainer, "%[%[[Uu]ser:(.+)|") or maintainer

		

		page_categories = page_categories.. "[[Category:WMF grant reports by grantee|"..maintainer.."]]"

		

		category_per_grantee = mw.title.new("Category:"..maintainer)

		if category_per_grantee.exists then page_categories = page_categories.. "[[Category:"..maintainer.."]]" end

	end

 	

	return page_categories

	

end



local function makeTextField(field, field_div)

	-- makes a formatted text field for output, based on parameter

	-- values or on default values provided for that field 

	-- type in the stylesheet

	field_div:cssText(field.style)

	if field.vtype2 == "body" then

		field_div

			:tag('span')

				:cssText(field.style2)--inconsistent use of styles 2/3 here

				:wikitext(field.title)--we probably aren't using this for most things now, after Heather's redesign

				:done()

			:tag('span')

				:cssText(field.style3)

				:wikitext(field.values1])

				:done()

	elseif field.vtype2 == "title" then

		field_div

			:tag('span')

				:cssText(field.style3)

				:wikitext(field.values1])

				:done()

	elseif field.vtype2 == "link" then

		field_div

			:tag('span')

				:cssText(field.style3)

				:wikitext("[[" .. field.values1 .. "|<span style='" .. field.style2 .. "'>" .. field.title .."</span>]]")

				:done()

	end

	field_div:done()

	return field_div

end



local function makeImageField(field, field_div)

	-- makes a formatted image field for output, based on parameter values

	-- provided in the calling template or its parent template, or on default

	-- values provided in the stylesheet

	field_div:cssText(field.style)

	field_div

		:tag('span')

		:cssText(field.style3)

	if field.vtype2 == "thumb" then

		field_div

			:wikitext("[[" .. field.values1 .. "|right|"  .. field.width .."]]")

	elseif field.vtype2 == "thumb2" then

		field_div

			:wikitext("[[" .. field.values1 .. "|center|"  .. field.width .."]]")

	elseif field.vtype2 == "link" then

		field_div

			:wikitext("[[" .. field.values1 .. "|" .. field.alignment .. "|" .. field.width .."|link=" .. field.link .. "]]")

	elseif field.vtype2 == "badge" then

		if mw.ustring.find( field.values1], "http", 1, true ) then

			field_div

				:wikitext("[[" .. field.icon .. "|" .. field.alignment .. "|" .. field.width .."|link=" .. field.values1 .. "]] " .. "[" .. field.values1 .. " " .. field.title .. "]")

		end

	elseif field.vtype2 == "ui_button" then

		field_div

			:addClass(field.class)

			:wikitext(field.title)

			:done()

	end

	field_div:done()

	return field_div

end



local function makeParticipantField(field, ftype)

	local field_div = mw.html.create('div')

	local title_span

	if field.icon then 

		title_span = "[[" .. field.icon .. "|left" .. "|18px]] " .. field.title

	else

		title_span = field.title

	end

	field_div

		:cssText(field.style)

		:tag('span')

			:cssText(field.style2)

			:wikitext(title_span)

			:done()

	if ftype == "filled" then

		local i = 1

		for k,v in ipairs(field.values) do

			if (i > 1 and field.icon) then --only insert extra padding if has icon

				field.style3 = "padding-left:25px; display:block"

			end

			if field.vtype2 then --ideally all configs should at least have this field for participants. FIXME

				if field.vtype2 == "username" then

					v = "• " .. "[[User:" .. v .. "|" .. v .. "]]"

				elseif field.vtype2 == "email" then

					v = "• " .. v

				end

			else

				v = "• " .. "[[User:" .. v .. "|" .. v .. "]]"

			end

			field_div

				:tag('span')

					:cssText(field.style3)

					:wikitext(v)

--					:wikitext("• " .. "[[User:" .. v .. "|" .. v .. "]]")

					:done()

			i = i + 1

		end

	end

	field_div:allDone()

	return field_div

end



local function makeSectionDiv(sec_fields, sec_style)

	local sec_div = mw.html.create('div'):cssText(sec_style)

	sec_fields = TableTools.compressSparseArray(sec_fields)

	for findex, sec_field in ipairs(sec_fields) do -- should put this at the end of the function, and just append the other stuff

		sec_div:node(sec_field)

	end

	return sec_div

end



local function makeParticipantsSection(frame, args, data, filled_role_data, open_roles)

	local filled_role_fields = {}

	for role, val_table in pairs(filled_role_data) do

		local field = data.fieldsrole

		field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})

		field.values = {}

		for val_num, val_text in ipairs(filled_role_datarole]) do

			field.values#field.values + 1 = val_text

		end

		local filled_field_div = makeParticipantField(field, "filled")

        filled_role_fieldsfield.rank = filled_field_div

	end

	local sec_div = makeSectionDiv(filled_role_fields, data.styles.section"participants"]) 

	if (data.fields.more_participants and args.more_participants and stringToLowerCase(args.more_participants)) == "yes" then -- really need this here?

		-- if (args.portal == "Idealab" or args.portal == "Research") then -- beware, exceptions everywhere

		sec_div:tag('span'):cssText("font-style:italic; color: #888888"):wikitext(mw.text.trim(frame:expandTemplate{title=args.translations, args={data.fields.more_participants.key}})):done()

--		elseif args.portal == "Patterns" then

--			sec_div:tag('span'):cssText("font-style:italic; color: #888888"):wikitext("a learning pattern for..."):done()

--		else

		for role, val in pairs(open_roles) do -- should make these ordered using compressSparseArray, as above

			local field = data.fieldsrole

			field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})

			if field.icon then

				field.icon = field.icon_inactive

			end

			local open_field_div = makeParticipantField(field, "open")

			sec_div:node(open_field_div)

		end

	end

	sec_div:allDone()

	return sec_div

end



local function makeSectionFields(args, field)

	-- ui button is separate

	local field_div = mw.html.create('div'):cssText(field.style) --why declare this here?

	if field.vtype == "image" then

		if (field.isRequired == true or (argsfield.arg and string.len(argsfield.arg]) > 0))  then --should move this up, may not just apply to images

			field_div = makeImageField(field, field_div)

		end

	elseif field.vtype == "text" then

		field_div = makeTextField(field, field_div)

	else

	end

	return field_div -- make sure div is 'done'

end



local function makeSection(frame, args, data, box_sec) 

	-- return a div for a section of the box including child divs

	-- for each content field in that section

	-- local sec_div = mw.html.create('div'):cssText(data.styles.section[box_sec])

	local sec_fields = {}

	for k,v in pairs(data.fields) do

		if data.fieldsk].section == box_sec then

			local field = data.fieldsk

			field.title = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.key}})

			field.values = {}

			if (argsk and string.len(argsk]) > 0) then

				field.values1 = argsk --does not accept numbered args

				if field.toLowerCase == true then -- special casing to make IEG status=SELECTED to display in lowercase

					field.values1 = stringToLowerCase(field.values1])

				end

				local field_div = makeSectionFields(args, field)

				sec_fieldsfield.rank = field_div

			elseif field.isRequired == true then

				if field.vtype == "text" then

					field.values1 = mw.text.trim(frame:expandTemplate{title=args.translations, args={field.default}})

				else

					field.values1 = field.default

				end

				local field_div = makeSectionFields(args, field)

				sec_fieldsfield.rank = field_div

			else

				--don't make a section for this field

			end

		end

	end

	local sec_div = makeSectionDiv(sec_fields, data.styles.sectionbox_sec]) 

	return sec_div

end



local function makeInfobox(frame, args, data, filled_role_data, open_roles)

	-- builds the infobox. Some content sections are required, others 

	-- are optional. Optional sections are defined in the stylesheet.

	local box = mw.html.create('div'):cssText(data.styles.box.outer)

	local inner_box = mw.html.create('div'):cssText(data.styles.box.inner)

	if data.sections.above == true then

		local sec_top = makeSection(frame, args, data, "above")

		box:node(sec_top)

	end

	if data.sections.nav == true then

		local sec_nav = makeSection(frame, args, data, "nav")

		box:node(sec_nav)

	end

	local sec_head = makeSection(frame, args, data, "head")

	inner_box:node(sec_head)

	local sec_main = makeSection(frame, args, data, "main")

	inner_box:node(sec_main)

	if data.sections.participants == true then

		local sec_participants = makeParticipantsSection(frame, args, data, filled_role_data, open_roles)

		inner_box:node(sec_participants)

	end

	if data.sections.cta == true then

		local sec_cta = makeSection(frame, args, data, "cta")

		sec_cta:addClass("noprint") -- no much use in print outs

		inner_box:node(sec_cta)

		inner_box:tag('div'):cssText("clear:both;"):done() --clears buttons in the cta sections

	end

	inner_box:allDone()

	box:node(inner_box)

	if data.sections.below == true then

		local sec_bottom = makeSection(frame, args, data, "below")

		box:node(sec_bottom)

	end

	box:allDone()

	return box

end



local function orderStringtoNumber(array, val, num)

    if num > table.getn(array) then

        array#array+1 = val

    else

        table.insert(array, num, val)

    end

    return array

end

    

local function isJoinable(args, data)

	if args.more_participants == "NO" then

		data.fields.join = nil

		if data.fields.endorse then

			data.fields.endorse.style = "display:inline; float:right;"

		end

	end

	return data

end



local function deepCopyTable(data)

	-- the deep copy is a workaround step to avoid the restrictions placed on 

	-- tables imported through loadData

	if type(data) ~= 'table' then return data end

	local res = {}

	for k,v in pairs(data) do

		if type(v) == 'table' then

		v = deepCopyTable(v)

		end

		resk = v

	end

	return res

end

	

local function getPortalData(args)

	-- loads the relevant stylesheet, if a sub-template was called with a portal

	-- argument and a stylesheet exists with the same name. For example, calling 

	-- {{#invoke:Probox|main|portal=Idealab}} would load the Module:Probox/Idealab

	-- stylesheet.

	local data_readOnly = {}

	local data_writable = {}

	if (args.portal and mw.title.makeTitle( 'Module', 'Probox/' .. args.portal).exists) then

		data_readOnly = mw.loadData("Module:Probox/" .. args.portal)

	else

		data_readOnly = mw.loadData("Module:Probox/Default")

	end

	-- data_writable = TableTools.shallowClone(data_readOnly)

	data_writable = deepCopyTable(data_readOnly)

	return data_writable

end



local function TCTlookup(args)

	local tct_path = tostring(args.translations)

	 --mw.log(mw.title.getCurrentTitle().subpageText)

	 local tct_subpage = mw.title.getCurrentTitle().subpageText

	if tct_subpage == "en" then

		tct_path = tct_path .. "/" .. tct_subpage

	elseif (mw.title.new("Template:" .. args.translations .. "/" .. tct_subpage).exists and mw.language.isSupportedLanguage(tct_subpage)) then

		tct_path = tct_path .. "/" .. tct_subpage

	else

		tct_path = tct_path .. "/" .. "en"

	end

	-- mw.log(tct_path)

	return tct_path

end



local function getRoleArgs(args, available_roles)

    -- returns:

    -- 1) a table of ordered values for valid role params, 

    -- even if numbered nonsequentially

    -- 2) a table of all roles with at least 1 empty param, 

    -- plus the default volunteer role

    local filled_role_data = {}

    local open_roles = {}

    if available_roles.default then -- some boxes have default role to join

    	open_rolesavailable_roles.default = true

    end

	for rd_key, rd_val in pairs(available_roles) do

		for a_key, a_val in pairs(args) do

    		if stringStarts(a_key, rd_key) then

                if string.len(a_val) == 0 then

                    open_rolesrd_key = true

                else   

                    if not filled_role_datard_key then filled_role_datard_key = {} end

                    local arg_num = tonumber(a_key:match('^' .. rd_key .. '([1-9]%d*)$'))

                    if arg_num then

                        filled_role_datard_key = orderStringtoNumber(filled_role_datard_key], a_val, arg_num)

                    else

                        table.insert(filled_role_datard_key], 1, a_val)

                    end

                end

            end

		end

	end

	return filled_role_data, open_roles

end



function p.main(frame)

	local args = getArgs(frame, {removeBlanks = false})

	local data = getPortalData(args)

 	data = isJoinable(args, data)

	if not (args.translations and mw.title.new("Template:" .. args.translations).exists) then

 		args.translations = "Probox/Default/Content"

 	end

 	-- mw.log(args.translations)

 	-- if the TCT content index is under translation, check for translations in the subpage language

 	if mw.title.new("Template:" .. args.translations .. "/en").exists then

 		args.translations = TCTlookup(args)

 	end

 	if data.sections.cta == true then

 		args.talk = tostring(mw.title.getCurrentTitle().talkPageTitle)  -- expensive

 	end

 	local filled_role_data, open_roles = getRoleArgs(args, data.roles)

	local box = makeInfobox(frame, args, data, filled_role_data, open_roles)

	local infobox = tostring(box)

	-- only add cats if not in Template or User or Meta namespace

	if (data.categories and (mw.title.getCurrentTitle().nsText ~= "Template" and mw.title.getCurrentTitle().nsText ~= "User" and mw.title.getCurrentTitle().nsText ~= "Meta") and not args.noindex) then

		-- FIXME specify namespace in config, so that categories only appear if template is translcuded in that namespace

		local page_categories = addCategories(frame, data, args, open_roles)

		infobox = infobox .. page_categories

	end

	if data.templates then

		local top_template = addTemplates(frame, data, args)

		infobox = top_template .. infobox

	end

	return infobox

end



return p

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook