Module:TeamBracket
--
 -- This module will implement {{TeamBracket}}
 --
  
 local p = {}
 local args
 local rounds
 local padding
 
 local function getArgs(frame)
 	local parent = frame:getParent();
 	local args = parent.args;
 	for k,v in pairs(frame.args) do 
 		args[k] = v
 	end
 	return args;
 end
 
 function getSeeds()
 	local seeds = {1, 2}
 	local count = 2
 	local before = false
 	for r = 2, rounds do
 		local max = math.pow(2, r)
 		for i = 1, count do
 			local pos = i * 2
 			if before then pos = pos - 1 end
 			table.insert(seeds, pos, max - seeds[i * 2 - 1] + 1)
 			before = not before
 		end
 		count = count * 2
 	end
 	return seeds
 end
 
 function addTableRow(tbl)
 	return tbl:tag('tr')
 end
 
 function addBlank(row, width)
 	local cell = row:tag('td')
 		:css('border-width', '0')
 		:css('border-style', 'solid')
 		:css('border-color', 'black')
 	if width then
 		cell:css('width', width)
 	end
 	return cell
 end
 
 function addPath(rows, index, round, top, left)
 	local prop = top and 'border-bottom-width' or 'border-top-width'
 	if left and round == 1 then
 		addBlank(rows[index]):css('height', '7px')
 		addBlank(rows[index + 1]):css('height', '7px')
 		return nil
 	else
 		local cell = addBlank(rows[index]):attr('rowspan', '2')
 		if left or round &lt; rounds and not left then
 			cell:css(prop, '2px')
 		end
 		return cell
 	end
 end
 
 function getWidth(param, default)
 	local arg = args[param .. '-width']
 	if not arg or string.len(arg) == 0 then
 		arg = default
 	end
 	if tonumber(arg) ~= nil then
 		arg = arg .. 'px'
 	end
 	return arg
 end
 
 function getTeamArg(round, type, team)
 	return args[getTeamArgName(round, type, team)]
 end
 
 function getTeamArgName(round, type, team)
 	return string.format('RD%d-%s' .. padding, round, type, team)
 end
 
 function getRoundName(round)
 	local name = args['RD' .. round]
 	if name and string.len(name) &gt; 0 then
 		return name
 	end
 	local roundFromLast = rounds - round + 1
 	if roundFromLast == 1 then
 		return &quot;Finals&quot;
 	elseif roundFromLast == 2 then
 		return &quot;Semifinals&quot;
 	elseif roundFromLast == 3 then
 		return &quot;Quarterfinals&quot;
 	else
 		return &quot;Round of &quot; .. math.pow(2, roundFromLast)
 	end
 end
  
 function renderTeam(row, round, team)
 	local seedArg = getTeamArg(round, 'seed', team)
 	-- seed value for the paired team
 	local pairSeedArg = getTeamArg(round, 'seed',
 		team % 2 == 0 and team - 1 or team + 1)
 	-- show seed if seed is defined for either or both
 	local showSeed = seedArg and string.len(seedArg) &gt; 0
 		or pairSeedArg and string.len(pairSeedArg) &gt; 0
 	if showSeed then
 		row:tag('td')
 			:attr('rowspan', '2')
 			:css('text-align', 'center')
 			:css('background-color', '#f2f2f2')
 			:css('border', '1px solid #aaa')
 			:wikitext(seedArg)
 			:newline()
 	end
 	local teamArg = getTeamArg(round, 'team', team)
 	if not teamArg or string.len(teamArg) == 0 then
 		teamArg = '&amp;nbsp;'
 	end
 	local teamCell = row:tag('td')
 		:attr('rowspan', '2')
 		:css('background-color', '#f9f9f9')
 		:css('border', '1px solid #aaa')
 		:css('padding', '0 2px')
 		:wikitext(teamArg)
 		:newline()
 	if not showSeed then
 		teamCell:attr('colspan', '2')
 	end
 	row:tag('td')
 		:attr('rowspan', '2')
 		:css('text-align', 'center')
 		:css('border', '1px solid #aaa')
 		:css('background-color', '#f9f9f9')
 		:wikitext(getTeamArg(round, 'score', team))
 		:newline()
 end
 
 function renderRound(rows, count, r)	
 	local teams = math.pow(2, rounds - r + 1)
 	local step = count / teams
 	local top = true
 	local open = false
 	local team = 1
 	for i = 1, count, step do
 		local offset, height, blank
 		-- leave room for groups for teams other than first and last
 		if team == 1 or team == teams then
 			offset = top and i or i + 2
 			height = step - 2
 		else
 			offset = top and i + 1 or i + 2
 			height = step - 3
 		end
 		if height &gt; 0 then
 			blank = addBlank(rows[offset])
 				:attr('colspan', '5')
 				:attr('rowspan', height)
 		end
 		-- add bracket
 		local j = top and i + step - 2 or i
 		addPath(rows, j, r, top, true)
 		renderTeam(rows[j], r, team)
 		local right = addPath(rows, j, r, top, false)
 		if not top then open = not open end
 		if open and r &lt; rounds then
 			if blank then blank:css('border-right-width', '2px') end
 			right:css('border-right-width', '2px')
 		end
 		team = team + 1
 		top = not top
 	end
 end	
 
 function renderGroups(rows, count, round)
 	local roundFromLast = rounds - round + 1
 	local groups = math.pow(2, roundFromLast - 2)
 	local step = count / groups
 	local group = 1
 	for i = step / 2, count, step do
 		local name = 'RD' .. round .. '-group' .. group
 		addBlank(rows[i]):css('height', '7px')
 		addBlank(rows[i + 1]):css('height', '7px')
 		addBlank(rows[i])
 			:attr('rowspan', '2')
 			:attr('colspan', 5 * round - 1)
 			:css('text-align', 'center')
 			:css('border-right-width', '2px')
 			:wikitext(args[name])
 			:newline()
 		group = group + 1
 	end
 end
 
 function renderTree(tbl)
 	-- create 3 rows for every team
 	local count = math.pow(2, rounds) * 3
 	local rows = {}
 	for i = 1, count do
 		rows[i] = addTableRow(tbl)
 	end
 	-- fill rows with groups
 	for r = 1, rounds - 1 do
 		renderGroups(rows, count, r)
 	end
 	-- fill rows with bracket
 	for r = 1, rounds do
 		renderRound(rows, count, r)
 	end
 end
 
 function renderHeading(tbl)
 	local titleRow = addTableRow(tbl)
 	local widthRow = addTableRow(tbl)
 	for r = 1, rounds do
 		addBlank(titleRow)
 		addBlank(widthRow, r &gt; 1 and '5px' or nil)
 		titleRow:tag('td')
 			:attr('colspan', '3')
 			:css('text-align', 'center')
 			:css('border', '1px solid #aaa')
 			:css('background-color', '#f2f2f2')
 			:wikitext(getRoundName(r))
 			:newline()
 		addBlank(widthRow, getWidth('seed', '25px')):wikitext('&amp;nbsp;')
 		addBlank(widthRow, getWidth('team', '150px')):wikitext('&amp;nbsp;')
 		addBlank(widthRow, getWidth('score', '25px')):wikitext('&amp;nbsp;')
 		addBlank(titleRow)
 		addBlank(widthRow, r &lt; rounds and '5px' or nil)
 	end
 end
 
 function p.teamBracket(frame)
 	args = getArgs(frame)
 	rounds = tonumber(args.rounds) or 2
 	local teams = math.pow(2, rounds)
 	padding = '%0' .. (teams &lt; 10 and 1 or 2) .. 'd'
 	
 	-- set default seeds for round 1
 	local seeds = getSeeds()
 	local argname;
 	for i = 1, table.getn(seeds) do
 		argname = getTeamArgName(1, 'seed', i)
 		if not args[argname] then
 			args[argname] = seeds[i]
 		end
 	end
 
 	local tbl = mw.html.create('table')
 		:css('border-style', 'none')
 		:css('font-size', '90%')
 		:css('margin', '1em 2em 1em 1em')
 		:css('border-collapse', 'separate')
 		:css('border-spacing', '0')
  
 	renderHeading(tbl)
 	renderTree(tbl)
 	return tostring(tbl)
 end