Module:Build bracket

local p = {} local entries = {} local pathEntries = {} local pathcolor local shift = {} local hascross = {} local teams_per_match = {} local rlegs = {} local maxlegs = {} local autolegs local byes = {} local hide = {} local matchgroup = {} local autocol local seeds local forceseeds local nowrap local boldwinner local aggregate local paramstyle local masterindex

local function isempty(s) return s==nil or s=='' end

local function notempty(s) return s~=nil and s~='' end

local function bargs(s) return pargs[s] or fargs[s] end

local function toChar(num) return string.char(string.byte("a")+num-1) end

local function split(str,delim,tonum) result = {}; local a = "[^"..table.concat(delim).."]+" for w in str:gmatch(a) do			if tonum==true then table.insert(result, tonumber(w)); else table.insert(result, w); end end return result; end

local function getWidth(ctype, default) local result = bargs(ctype..'-width') if isempty(result) then return default end if tonumber(result)~=nil then return result..'px' end return result end

local function matchGroups for j=minc,c do		matchgroup[j]={} for i=1,r do			if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then matchgroup[j][i]=math.ceil(entries[j][i]['index']/teams_per_match[j]) entries[j][i]['group'] = math.ceil(entries[j][i]['index']/teams_per_match[j]) end end end end

local function boldWinner local function boldScore(j,i,l) if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then local myscore = entries[j][i]['score'][l] if myscore == "" or myscore:find("%D") then return 'normal' else myscore=tonumber(myscore) end local compscore = {} for k,v in pairs(matchgroup[j]) do				if matchgroup[j][i]==v and k~=i then if entries[j][k]['score'][l]==nil or entries[j][k]['score'][l] == "" or entries[j][k]['score'][l]:find("%D") then return 'normal' else table.insert(compscore,tonumber(entries[j][k]['score'][l])) end end end for k,v in pairs(compscore) do				if myscore<=v then return 'normal' end end if l~='agg' then entries[j][i]['wins'] = entries[j][i]['wins']+1 else entries[j][i]['aggwins'] = 1 end return 'bold' end end local function boldEntry(j,i,agg) local wins if agg~=true then wins = 'wins' else wins = 'aggwins' end local myteam = entries[j][i][wins] local compteam = {} for k,v in pairs(matchgroup[j]) do			if matchgroup[j][i]==v and k~=i then table.insert(compteam,tonumber(entries[j][k][wins])) end end for k,v in pairs(compteam) do			if myteam<=v then return 'normal' end end return 'bold' end for j=minc,c do		for i=1,r do			if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then entries[j][i]['wins'] = 0 entries[j][i]['aggwins'] = 0 end end for i=1,r do			if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then local legs = rlegs[j] if notempty(entries[j][i]['legs']) then legs = tonumber(entries[j][i]['legs']) end if autolegs then local l=1 repeat l=l+1 until isempty(entries[j][i]['score'][l]) legs = l-1 end for l=1,legs do					entries[j][i]['score']['weight'][l] = boldScore(j,i,l) end if aggregate and legs>1 then entries[j][i]['score']['weight']['agg'] = boldScore(j,i,'agg') end end end for i=1,r do			if entries[j][i]~= nil and entries[j][i]['ctype']=='team' then local agg local legs = rlegs[j] if notempty(entries[j][i]['legs']) then legs = tonumber(entries[j][i]['legs']) end if autolegs then local l=1 repeat l=l+1 until isempty(entries[j][i]['score'][l]) legs = l-1 end if aggregate and legs>1 then agg=true end entries[j][i]['weight'] = boldEntry(j,i,agg) end end end end

local function isBlankEntry(col,row,ctype) if isempty(entries[col][row]) then return true end if isempty(entries[col][row]['team']) and isempty(entries[col][row]['text']) then return true end return false end

local function showSeeds(j,i) local showseed=false if forceseeds or notempty(entries[j][i]['seed']) then showseed=true else for k=1,teams_per_match[j]-1 do			if notempty(entries[j][i+2*k]) and entries[j][i]['group']==entries[j][i+2*k]['group'] and notempty(entries[j][i+2*k]['seed']) then showseed=true end if notempty(entries[j][i-2*k]) and entries[j][i]['group']==entries[j][i-2*k]['group'] and notempty(entries[j][i-2*k]['seed']) then showseed=true end end end return showseed end

local function blankCell(tbl,j,i,rowspan,colspan,text,align) local cell = tbl:tag('td') if colspan~=1 then cell:attr('colspan',colspan) end if rowspan~=1 then cell:attr('rowspan',rowspan) end if notempty(align) then cell:css('text-align',align) end if notempty(text) then if nowrap then cell:css('white-space', 'nowrap') end cell:wikitext(text) end return cell end

local function headerCell(tbl,j,i,colspan) local cell = tbl:tag('td') :attr('rowspan',2) :css('text-align','center') :css('border','1px solid #aaa') :css('background-color',entries[j][i]['shade']) :wikitext(entries[j][i]['header']) if colspan~=1 then cell:attr('colspan',colspan) end if nowrap then cell:css('white-space', 'nowrap') end if entries[j][i]['position']=='top' and entries[j][i-2]~=nil and entries[j][i-2]['ctype']~='blank' and entries[j][i-2]['ctype']~='text' and entries[j][i-2]['ctype']~='group' and (not byes[j][entries[j][i]['headerindex']] or not isBlankEntry(j,i-2)) then cell:css('border-top','2px solid #aaa') end return cell end

local function teamCell(tbl,k,j,i,l,colspan) local bg = '#F2F2F2' local cell = tbl:tag('td') :attr('rowspan',2) :css('border','1px solid #aaa') if colspan~=1 and colspan~=nil then cell:attr('colspan',colspan) end if k=='seed' or k=='score' then cell:css('text-align','center') end if k=='seed' or k=='score' or nowrap then cell:css('white-space', 'nowrap') end if k=='seed' then cell:css('border-right','2px solid #aaa') else bg='#F9F9F9' end if k=='team' then cell:css('padding-left','0.3em') end if l=='agg' then cell:css('border-left','2px solid #aaa') end if (l==nil and entries[j][i]['weight']=='bold') or entries[j][i]['score']['weight'][l]=='bold' then cell:css('font-weight','bold') end if entries[j][i]['position']=='top' and entries[j][i-2]~=nil and entries[j][i-2]['ctype']~='blank' and entries[j][i-2]['ctype']~='text' and entries[j][i-2]['ctype']~='group' and (not byes[j][entries[j][i]['headerindex']] or not isBlankEntry(j,i-2)) then cell:css('border-top','2px solid #aaa') end cell:css('background-color',bg) if l==nil then cell:wikitext(entries[j][i][k]) else cell:wikitext(tostring(entries[j][i][k][l])) end return cell end

local function insertEntry(tbl,j,i) local entry_colspan=maxlegs[j]+2 if not seeds then entry_colspan=entry_colspan-1	end if (aggregate and maxlegs[j]>1) or maxlegs[j]==0 then entry_colspan=entry_colspan+1 end if entries[j][i]~=nil and entries[j][i]['ctype']=='blank' then return end if entries[j][i]==nil then if entries[j][i-1]~=nil or i==1 then local rowspan = 0 local row = i			repeat rowspan=rowspan+1 row=row+1 until entries[j][row]~=nil or row>r return blankCell(tbl,j,i,rowspan,entry_colspan) else return end end

if entries[j][i]['ctype']=='header' then if byes[j][entries[j][i]['headerindex']] then local emptyround = true local row = i+1 repeat if not isBlankEntry(j,row) then emptyround = false end row = row+1 until (entries[j][row]~=nil and entries[j][row]['ctype']=='header') or row>r if emptyround == true then return blankCell(tbl,j,i,2,entry_colspan) end end if hide[j][entries[j][i]['headerindex']] then return blankCell(tbl,j,i,2,entry_colspan) end if isempty(entries[j][i]['header']) then if entries[j][i]['headerindex']==1 then if    j==c   then entries[j][i]['header'] = 'Final' elseif j==c-1 then entries[j][i]['header'] = 'Semifinals' elseif j==c-2 then entries[j][i]['header'] = 'Quarterfinals' else			  entries[j][i]['header'] = 'Round '..j				end else entries[j][i]['header'] = 'Lower round '..j			end end return headerCell(tbl,j,i,entry_colspan) end if entries[j][i]['ctype']=='team' then if byes[j][entries[j][i]['headerindex']] and isBlankEntry(j,i) then return blankCell(tbl,j,i,2,entry_colspan) end if hide[j][entries[j][i]['headerindex']] then return blankCell(tbl,j,i,2,entry_colspan) end local legs = rlegs[j] local team_colspan if notempty(entries[j][i]['legs']) then legs = tonumber(entries[j][i]['legs']) end if autolegs then local l=1 repeat l=l+1 until isempty(entries[j][i]['score'][l]) legs = l-1 end team_colspan = maxlegs[j]-legs+1 if aggregate and legs==1 and maxlegs[j]>1 then team_colspan=team_colspan+1 end if maxlegs[j]==0 then team_colspan=team_colspan+1 end if seeds then if showSeeds(j,i)==true then teamCell(tbl,'seed',j,i) else team_colspan=team_colspan+1 end end teamCell(tbl,'team',j,i,nil,team_colspan) for l=1,legs do			teamCell(tbl,'score',j,i,l) end if aggregate and legs>1 then teamCell(tbl,'score',j,i,'agg') end end if entries[j][i]['ctype']=='text' then blankCell(tbl,j,i,2,entry_colspan,entries[j][i]['text']) end if entries[j][i]['ctype']=='group' then local colspan=0 for m=j,entries[j][i]['colspan']+j-1 do			colspan=colspan+maxlegs[m]+2 if not seeds then colspan=colspan-1 end if (aggregate and maxlegs[m]>1) or maxlegs[m]==0 then colspan=colspan+1 end end colspan = colspan+2*(entries[j][i]['colspan']-1) blankCell(tbl,j,i,2,colspan,entries[j][i]['group'],'center') end if entries[j][i]['ctype']=='line' then tbl:tag('td') :attr('rowspan',2) :attr('colspan',entry_colspan) :css('border-bottom','3px solid black') :wikitext(entries[j][i]['text']) end end

local function isRoundHidden(j,i,headerindex) if notempty(entries[j][i]['pheader']) then hide[j][entries[j][i]['headerindex']] = false end local row = i+1 repeat if not isBlankEntry(j,row) then hide[j][entries[j][i]['headerindex']] = false end row = row+1 until (entries[j][row]~=nil and entries[j][row]['ctype']=='header') or row>r end

local function paramNames(cname,j,i,l) local rname = { {'RD'..j, bargs('RD'..j..'-altname') or 'RD'..j}, {'RD'..j..toChar(entries[j][i]['headerindex']),bargs('RD'..j..toChar(entries[j][i]['headerindex'])..'-altname') or 'RD'..j..toChar(entries[j][i]['headerindex'])} }	local name = {cname, bargs(cname..'-altname') or cname} local index = {entries[j][i]['index'], entries[j][i]['altindex']} local result = {} if cname=='header' then if entries[j][i]['headerindex']==1 then for k=1,2 do				table.insert(result,bargs(rname[1][3-k]) or '') table.insert(result,bargs(rname[2][3-k]) or '') end else for k=1,2 do				table.insert(result,bargs(rname[2][3-k]) or '') end end elseif cname=='pheader' then if entries[j][i]['headerindex']==1 then for k=1,2 do				table.insert(result,pargs[rname[1][3-k]] or '') table.insert(result,pargs[rname[2][3-k]] or '') end else for k=1,2 do				table.insert(result,pargs[rname[2][3-k]] or '') end end elseif cname=='score' then for m=1,2 do for k=1,2 do			if l==1 then table.insert(result,bargs(rname[3-m][3-k]..'-'..name[1]..index[3-m]) or bargs(rname[3-m][3-k]..'-'..name[1]..'0'..index[3-m]) or '') end table.insert(result,bargs(rname[3-m][3-k]..'-'..name[1]..index[3-m]..'-'..l) or bargs(rname[3-m][3-k]..'-'..name[1]..'0'..index[3-m]..'-'..l) or '') end	end elseif cname=='shade' then for k=1,2 do			if entries[j][i]['headerindex']==1 then table.insert(result,bargs(rname[1][3-k]..'-'..name[1]) or '') else table.insert(result,bargs(rname[2][3-k]..'-'..name[1]) or '') end end table.insert(result,bargs('RD-shade')) table.insert(result,'#F2F2F2') elseif cname=='text' then for n=1,2 do for m=1,2 do for k=1,2 do			table.insert(result,bargs(rname[3-m][3-k]..'-'..name[3-n]..index[3-m]) or bargs(rname[3-m][3-k]..'-'..name[3-n]..'0'..index[3-m]) or '') end end	end else for m=1,2 do for k=1,2 do			table.insert(result,bargs(rname[3-m][3-k]..'-'..name[1]..index[3-m]) or bargs(rname[3-m][3-k]..'-'..name[1]..'0'..index[3-m]) or '') end	end end for k=1,#result do		if notempty(result[k]) then return result[k] end end return '' end

local function indexedParams(j) for i=1,r do		if entries[j][i]~=nil then if entries[j][i]['ctype']=='team' then local legs = rlegs[j] if forceseeds then entries[j][i]['seed'] = bargs(masterindex) or '' masterindex = masterindex+1 end entries[j][i]['team'] = bargs(tostring(masterindex)) or '' masterindex = masterindex+1 entries[j][i]['legs'] = paramNames('legs',j,i) entries[j][i]['score'] = {} entries[j][i]['weight'] = 'normal' entries[j][i]['score']['weight'] = {} if notempty(entries[j][i]['legs']) then legs = tonumber(entries[j][i]['legs']) end for l=1,legs do					entries[j][i]['score'][l] = bargs(tostring(masterindex)) or '' masterindex = masterindex+1 entries[j][i]['score']['weight'][l] = 'normal' end if aggregate and legs>1 then entries[j][i]['score']['agg'] = bargs(masterindex) or '' masterindex = masterindex+1 entries[j][i]['score']['weight']['agg'] = 'normal' end end if entries[j][i]['ctype']=='header' then entries[j][i]['header'] = paramNames('header',j,i) entries[j][i]['pheader'] = paramNames('pheader',j,i) entries[j][i]['shade'] = paramNames('shade',j,i) end if entries[j][i]['ctype']=='text' then entries[j][i]['text'] = bargs(tostring(masterindex)) or '' masterindex = masterindex+1 end if entries[j][i]['ctype']=='group' then entries[j][i]['group'] = bargs(tostring(masterindex)) or '' masterindex = masterindex+1 end if entries[j][i]['ctype'] == 'line' and entries[j][i]['hastext']==true then entries[j][i]['text'] = bargs(masterindex) or '' masterindex = masterindex+1 end end end end

local function assignParams masterindex = 1 local maxcol = 1 local byerows = 1 local hiderows = 1 for j=minc,c do		rlegs[j] = tonumber(bargs('RD'..j..'-legs')) or tonumber(bargs('legs')) or 1 if notempty(bargs('RD'..j..'-legs')) or bargs('legs') then autolegs = false end if paramstyle == 'numbered' then indexedParams(j) else for i=1,r do				if entries[j][i]~=nil then if entries[j][i]['ctype']=='team' then local legs = rlegs[j] entries[j][i]['seed'] = paramNames('seed',j,i) entries[j][i]['team'] = paramNames('team',j,i) entries[j][i]['legs'] = paramNames('legs',j,i) entries[j][i]['score'] = {} entries[j][i]['weight'] = 'normal' entries[j][i]['score']['weight'] = {} if notempty(entries[j][i]['legs']) then legs = tonumber(entries[j][i]['legs']) end if autolegs then local l=1 repeat entries[j][i]['score'][l] = paramNames('score',j,i,l) entries[j][i]['score']['weight'][l] = 'normal' l=l+1 until isempty(paramNames('score',j,i,l)) legs = l-1 else for l=1,legs do								entries[j][i]['score'][l] = paramNames('score',j,i,l) entries[j][i]['score']['weight'][l] = 'normal' end end if aggregate and legs>1 then entries[j][i]['score']['agg'] = paramNames('score',j,i,'agg') entries[j][i]['score']['weight']['agg'] = 'normal' end end if entries[j][i]['ctype']=='header' then entries[j][i]['header'] = paramNames('header',j,i) entries[j][i]['pheader'] = paramNames('pheader',j,i) entries[j][i]['shade'] = paramNames('shade',j,i) end if entries[j][i]['ctype']=='text' then entries[j][i]['text'] = paramNames('text',j,i) end if entries[j][i]['ctype']=='group' then entries[j][i]['group'] = paramNames('group',j,i) end if entries[j][i]['ctype'] == 'line' and entries[j][i]['hastext']==true then entries[j][i]['text'] = paramNames('text',j,i) end end if autocol and not isBlankEntry(j,i) then maxcol = math.max(maxcol,j) end end end for i=1,r do			if entries[j][i]~=nil and entries[j][i]['ctype']=='header' then isRoundHidden(j,i) end if entries[j][i]~=nil and not hide[j][entries[j][i]['headerindex']] then if not byes[j][entries[j][i]['headerindex']] or (byes[j][entries[j][i]['headerindex']] and not isBlankEntry(j,i)) then byerows = math.max(byerows,i) end end end end for j=minc,c do		for k=1,headerindex[j] do			if byes[j][k] or hide[j][k] then r=byerows+1 end end end if autocol then c = maxcol end end

local function getHide(j,headerindex) hide[j] = {} for k=1,headerindex[j] do		if bargs('RD'..j..toChar(k)..'-hide')=='yes' or bargs('RD'..j..toChar(k)..'-hide')=='y' then hide[j][k]=true end end end

local function getByes(j,headerindex) byes[j] = {} for k=1,headerindex[j] do		if bargs('byes')=='yes' or bargs('byes')=='y' then byes[j][k]=true else byes[j][k]=false end if bargs('RD'..j..'-byes')=='yes' or bargs('RD'..j..'-byes')=='y' then byes[j][k]=true elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then byes[j][k]=false end if bargs('RD'..j..toChar(k)..'-byes')=='yes' or bargs('RD'..j..toChar(k)..'-byes')=='y' then byes[j][k]=true elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then byes[j][k]=false end end end

local function getAltIndices local teamindex=1 local textindex=1 local groupindex=1 for j=minc,c do		headerindex[j]=0 for i=1,r do			if entries[j][i]==nil and i==1 then headerindex[j]=headerindex[j]+1 end if entries[j][i]~=nil then if entries[j][i]['ctype'] == 'header' then entries[j][i]['altindex'] = headerindex[j] teamindex=1 textindex=1 headerindex[j]=headerindex[j]+1 elseif entries[j][i]['ctype'] == 'team' then entries[j][i]['altindex'] = teamindex teamindex=teamindex+1 elseif entries[j][i]['ctype'] == 'text' then entries[j][i]['altindex'] = textindex textindex=textindex+1 elseif entries[j][i]['ctype'] == 'group' then entries[j][i]['altindex'] = groupindex groupindex=groupindex+1 elseif entries[j][i]['ctype'] == 'line' and entries[j][i]['hastext']==true then entries[j][i]['altindex'] = textindex textindex=textindex+1 end entries[j][i]['headerindex'] = headerindex[j] end end getByes(j,headerindex) getHide(j,headerindex) end end

local function pathCell(tbl,j,i,k,a,b,c,bg,rowspan) if not hascross[j] and k==2 then return end if pathcolor==nil then pathcolor='black' end local cell=tbl:tag('td') if rowspan~=1 then cell:attr('rowspan',rowspan) end if notempty(bg[k]) then cell:css('background',bg[k]) end if a[k][1]~=0 or a[k][2]~=0 or a[k][3]~=0 or a[k][4]~=0 then cell:css('border','solid black') :css('border-width',a[k][1]..'px '..a[k][2]..'px '..a[k][3]..'px '..a[k][4]..'px') end if c[k][2]~=0 then cell:css('border-right',c[k][2]..'px solid '..pathcolor) end if b[k][2]~=0 then cell:css('box-shadow', '0px '..b[k][2]..'px 0px '..pathcolor) end if c[k][4]~=0 then cell:css('border-left',c[k][4]..'px solid '..pathcolor) end return cell end

local function insertPath(tbl,j,i) local function createCross(angle,color,percent) local result = 'linear-gradient('..angle..', transparent calc('..percent..'% - 1px),'..color..' calc('..percent..'% - 1px),'..color..' calc('..percent..'% + 1px), transparent calc('..percent..'% + 1px))' return result end local colspan = 2 if hascross[j] then colspan = 3 end if pathEntries['black'][j][i]=='blank' then return end if isempty(pathEntries['black'][j][i]) and isempty(pathEntries['color'][j][i]) then if (pathEntries['black'][j][i-1]~='blank' and notempty(pathEntries['black'][j][i-1]) and (pathEntries['black'][j][i-1]['cross'][1] == 1 or pathEntries['black'][j][i-1]['cross'][2] == 1)) or (notempty(pathEntries['color'][j][i-1]) and (pathEntries['color'][j][i-1]['cross'][1] == 1 or pathEntries['color'][j][i-1]['cross'][2] == 1)) then return else local cell = tbl:tag('td') for k=1,colspan-1 do				cell:tag('td') end return cell end end for key,_ in pairs(pathEntries) do		if notempty(pathEntries[key][j]) and notempty(pathEntries[key][j][i-1]) and pathEntries[key][j][i-1]~='blank' then if pathEntries[key][j][i-1]['cross'][1]==1 or pathEntries[key][j][i-1]['cross'][2]==1 then return end end end local a = {} local b = {} local c = {} local bg = {} local cross = {} local rowspan = 1 for k=1,3 do		a[k]={0,0,0,0} b[k]={0,0} c[k]={0,0,0,0} bg[k] = '' cross[k] = {} for n=1,2 do			cross[k][n] = '' end end for key,_ in pairs(pathEntries) do		local color if key=='black' then color='black' else color=pathcolor end if notempty(pathEntries[key][j]) and notempty(pathEntries[key][j][i]) then if key=='black' then a[1][3] = 3*pathEntries['black'][j][i]['out'][1] a[2][3] = 3*pathEntries['black'][j][i]['mid'][1] a[3][3] = 3*pathEntries['black'][j][i]['in'][1] a[1][2] = 2*pathEntries['black'][j][i]['out'][2] a[3][4] = 2*pathEntries['black'][j][i]['in'][2] else if pathEntries['color'][j][i]['out'][3]=='below' then b[1][2] = 4*pathEntries['color'][j][i]['out'][1] end if pathEntries['color'][j][i]['mid'][3]=='below' then b[2][2] = 4*pathEntries['color'][j][i]['mid'][1] end if pathEntries['color'][j][i]['in'][3]=='below' then b[3][2] = 4*pathEntries['color'][j][i]['in'][1] end c[1][2] = 2*pathEntries['color'][j][i]['out'][2] c[3][4] = 2*pathEntries['color'][j][i]['in'][2] end if pathEntries[key][j][i]['cross'][1]==1 or pathEntries[key][j][i]['cross'][2]==1 then rowspan = 2 if pathEntries[key][j][i]['cross'][1]==1 then cross[1][1] = createCross('65.5deg',color,100) cross[2][1] = createCross('to top right',color,50) cross[3][1] = createCross('245.5deg',color,100) end if pathEntries[key][j][i]['cross'][2]==1 then cross[1][2] = createCross('114.4deg',color,100) cross[2][2] = createCross('to bottom right',color,50) cross[3][2] = createCross('294.4deg',color,100) end end end end if notempty(pathEntries['color'][j]) and notempty(pathEntries['color'][j][i-1]) then if pathEntries['color'][j][i]['out'][3]=='above' then b[1][2] = -4 end if pathEntries['color'][j][i]['mid'][3]=='above' then b[2][2] = -4 end if pathEntries['color'][j][i]['in'][3]=='above' then b[3][2] = -4 end end if notempty(cross[2][1]) and notempty(cross[2][2]) then for k=1,3 do cross[k][1] = cross[k][1]..',' end end for k=1,3 do		bg[k] = cross[k][1]..cross[k][2] end for k=1,3 do		pathCell(tbl,j,i,k,a,b,c,bg,rowspan) end end

local function getPaths local paths = {} pathEntries['black'] = {} pathEntries['color'] = {} for j=minc,c-1 do		pathEntries['black'][j] = {} pathEntries['color'][j] = {} for i=1,r do			pathEntries['black'][j][i] = {['out']={0,0,nil},['mid']={0,0,nil},['in']={0,0,nil},['cross']={0,0,nil}} pathEntries['color'][j][i] = {['out']={0,0,nil},['mid']={0,0,nil},['in']={0,0,nil},['cross']={0,0,nil}} end paths[j] = {} hascross[j] = false local crossloc = split((fargs['col'..j..'-col'..(j+1)..'-cross'] or ''):gsub("%s+", ""),{","},true) local str = fargs['col'..j..'-col'..(j+1)..'-paths'] or '' for val in str:gsub("%s+","") :gsub(",",", ") :gsub("%S+","\0%0\0") :gsub("%b", function(s) return s:gsub("%z","") end) :gmatch("%z(.-)%z") do			local result = split(val:gsub("%s+",""):gsub("%)",""):gsub("%(",""),{"-"}) for k,_ in pairs(result) do				result[k] = split(result[k],{","}) end if notempty(result[2]) then for m=1,#result[2] do					result[3] = {} result[2][m] = split(result[2][m],{":"}) result[3][m] = result[2][m][2] result[2][m] = result[2][m][1] end for n=1,#result[1] do					for m=1,#result[2] do						table.insert(paths[j],{tonumber(result[1][n]),tonumber(result[2][m]),['color']=result[3][m]}) end end end end if shift[j]~=0 and notempty(crossloc[1]) then for n=1,#crossloc do				crossloc[n] = crossloc[n]+shift[j] end end for k,v in ipairs(paths[j]) do			local color local hidepath = false if shift[j]~=0 then paths[j][k][1] = paths[j][k][1]+shift[j] end if shift[j+1]~=0 then paths[j][k][2] = paths[j][k][2]+shift[j+1] end local start = 2*paths[j][k][1]+(teams_per_match[j]-2) local mid = {} if notempty(crossloc[1]) then for n=1,#crossloc do					mid[n] = 2*crossloc[n]+(teams_per_match[j]-2) end else mid[1]=0 end local stop = 2*paths[j][k][2]+(teams_per_match[j+1]-2) if paths[j][k]['color']~=nil then pathcolor = paths[j][k]['color'] color = 'color' else color = 'black' end if notempty(entries[j][start-1]) and (byes[j][entries[j][start-1]['headerindex']] and isBlankEntry(j,start-1) and isBlankEntry(j,start+1) or hide[j][entries[j][start-1]['headerindex']]) then if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then hidepath=true end end if notempty(entries[j+1][stop-1]) and (byes[j+1][entries[j+1][stop-1]['headerindex']] and isBlankEntry(j+1,stop-1) and isBlankEntry(j+1,stop+1) or hide[j+1][entries[j+1][stop-1]['headerindex']])then if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then hidepath=true end end if bargs('RD'..j..'-RD'..(j+1)..'-path')=='n' or bargs('RD'..j..'-RD'..(j+1)..'-path')=='no' or bargs('RD'..j..'-RD'..(j+1)..'-path')=='0' then if notempty(entries[j][start-1]) and entries[j][start-1]['headerindex']==1 then hidepath=true end end local cross = 0 for n=1,#mid do				if (startstart) or (start>stop and mid[n]>stop and mid[n]r then break end start = math.min(start,r) pathEntries[color][j][start]['out'][1] = 1 pathEntries[color][j][start]['in'][1] = 1 if cross==0 then pathEntries[color][j][start]['mid'][1] = 1 end elseif startr then break end pathEntries[color][j][start]['out'][1] = 1 pathEntries[color][j][stop]['in'][1] = 1 pathEntries[color][j][stop]['in'][2] = 1 if color~='black' then pathEntries[color][j][start]['out'][3] = 'below' pathEntries[color][j][stop+1]['in'][3] = 'above' end if cross==0 then pathEntries[color][j][start]['mid'][1] = 1 pathEntries[color][j][start]['mid'][3] = 'below' for i=start+1,stop-1 do pathEntries[color][j][i]['in'][2] = 1 end else pathEntries[color][j][cross]['cross'][1] = 1 for i=start+1,cross-1 do pathEntries[color][j][i]['out'][2] = 1 end for i=cross+2,stop-1 do pathEntries[color][j][i]['in'][2] = 1 end end elseif start>stop then if start>r then break end pathEntries[color][j][stop]['in'][1] = 1 if cross==0 then for i=stop+1,start-1 do pathEntries[color][j][i]['in'][2] = 1 end pathEntries[color][j][start]['out'][1] = 1 pathEntries[color][j][start]['mid'][1] = 1 pathEntries[color][j][start]['in'][2] = 1 if color~='black' then pathEntries[color][j][start+1]['out'][3] = 'above' pathEntries[color][j][start+1]['mid'][3] = 'above' pathEntries[color][j][stop]['in'][3] = 'below' end else pathEntries[color][j][cross]['cross'][2] = 1 for i=stop+1,cross-1 do pathEntries[color][j][i]['in'][2] = 1 end for i=cross+2,start-1 do pathEntries[color][j][i]['out'][2] = 1 end pathEntries[color][j][start]['out'][1] = 1 pathEntries[color][j][start]['out'][2] = 1 if color~='black' then pathEntries[color][j][start+1]['out'][3] = 'above' pathEntries[color][j][stop]['in'][3] = 'below' end end end end end end for j=minc,c-1 do		for i=1,r do			local empty = true for key,_ in pairs(pathEntries) do				for k,v in pairs(pathEntries[key][j][i]) do					if v[1]~=0 or v[2]~=0 or v[3]~=nil then empty = false end end if empty then pathEntries[key][j][i] = nil end end end end for j=minc,c-1 do		if notempty(pathEntries['black'][j]) and notempty(pathEntries['black'][j+1]) then for i=1,r do				if (entries[j+1][i-1]==nil or (byes[j+1][entries[j+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) and notempty(pathEntries['black'][j][i]) and notempty(pathEntries['black'][j+1][i]) then if pathEntries['black'][j][i]['in'][1]==1 and pathEntries['black'][j+1][i]['out'][1]==1 then entries[j+1][i-1]={['ctype']='line'} entries[j+1][i]={['ctype']='blank'} end end end end end end

local function getGroups local function check(j,i) local result=false if entries[j][i] == nil then if entries[j][i+1] == nil then result=true elseif entries[j][i+1]['ctype']=='text' and isBlankEntry(j,i+1) then result=true end elseif entries[j][i]['ctype']=='text' and isBlankEntry(j,i) then result=true end return result end for j=minc,c-1 do		if teams_per_match[j]==2 then local n=0 for i=1,r do				if (notempty(pathEntries['black'][j][i]) and pathEntries['black'][j][i]['in'][1] == 1) or (notempty(pathEntries['color'][j][i]) and pathEntries['color'][j][i]['in'][1] == 1) then n=n+1 if check(j,i) then local k=minc-1 repeat if entries[j-k][i+1]~=nil and entries[j-k][i+1]['ctype']=='text' and isBlankEntry(j-k,i+1) then entries[j-k][i+2]=nil end entries[j-k][i]={['ctype']='blank'} entries[j-k][i+1]={['ctype']='blank'} if k>0 and isempty(pathEntries['black'][j-k][i]) and isempty(pathEntries['color'][j-k][i]) then pathEntries['black'][j-k][i] = 'blank' pathEntries['black'][j-k][i+1] = 'blank' end k=k+1 until k>j-1 or not check(j-k,i) or notempty(pathEntries['black'][j-k][i]) or notempty(pathEntries['color'][j-k][i]) k=k-1 entries[j-k][i]={['ctype']='group',['index']=n,['colspan']=k+1} entries[j-k][i+1]={['ctype']='blank'} entries[j-k][i]['group'] = bargs('RD'..j..'-group'..n)					end end end end end end

local function getCells local maxrow = 1 local colentry = {} local bool = true for j=minc,c do		if notempty(fargs['col'..j..'-headers']) then bool=false end teams_per_match[j] = tonumber(fargs['RD'..j..'-teams-per-match']) or tonumber(fargs['col'..j..'-teams-per-match']) or tonumber(fargs['teams-per-match']) or 2 maxtpm = math.max(maxtpm,teams_per_match[j]) end for j=minc,c do		entries[j] = {} shift[j] = tonumber(bargs('RD'..j..'-shift')) or tonumber(bargs('shift')) or 0 colentry[j] = { split((fargs['col'..j..'-headers'] or ''):gsub("%s+", ""),{","},true), split((fargs['col'..j..'-matches'] or ''):gsub("%s+", ""),{","},true), split((fargs['col'..j..'-lines'] or ''):gsub("%s+", ""),{","},true), split((fargs['col'..j..'-text'] or ''):gsub("%s+", ""),{","},true), }		if bool==true and fargs['noheaders']~='y' and fargs['noheaders']~='yes' then table.insert(colentry[j][1],1) end end

for j=minc,c do		local textindex=0 for k,v in ipairs(colentry[j]) do			table.sort(colentry[j][k]) local ctype if k==1 then ctype='header' elseif k==2 then ctype='team' elseif k==3 then ctype='line' elseif k==4 then ctype='text' elseif k==5 then ctype='group' end for n=1,#colentry[j][k] do				if shift[j]~=0 and colentry[j][k][n]>1 then colentry[j][k][n] = colentry[j][k][n]+shift[j] end local i=2*colentry[j][k][n]-1 maxrow = math.max(i+2*teams_per_match[j]-1,maxrow) if ctype=='team' then if entries[j][i-1]==nil and entries[j][i-2]==nil then entries[j][i-2]={['ctype']='text',['index']=n} entries[j][i-1]={['ctype']='blank'} textindex=n end entries[j][i]={['ctype']=ctype,['index']=teams_per_match[j]*n-(teams_per_match[j]-1),['position']='top'} entries[j][i+1]={['ctype']='blank'} for m=2,teams_per_match[j] do						entries[j][i+2*(m-1)]={['ctype']=ctype,['index']=teams_per_match[j]*n-(teams_per_match[j]-m)} entries[j][i+2*(m-1)+1]={['ctype']='blank'} end elseif ctype=='text' then entries[j][i]={['ctype']=ctype,['index']=textindex+n} entries[j][i+1]={['ctype']='blank'} elseif ctype=='line' then entries[j][i]={['ctype']=ctype} entries[j][i+1]={['ctype']='blank'} elseif ctype=='group' then entries[j][i]={['ctype']=ctype,['index']=n} entries[j][i+1]={['ctype']='blank'} else entries[j][i]={['ctype']=ctype,['index']=n,['position']='top'} entries[j][i+1]={['ctype']='blank'} end end end end if isempty(r) then r = maxrow end end

function p.main(frame) fargs = frame.args pargs = frame:getParent.args; r = tonumber(fargs.rows) or '' c = tonumber(fargs.rounds) or 1 maxc = tonumber(pargs.maxrounds) or tonumber(pargs.maxround) or '' minc = tonumber(pargs.minround) or 1 headerindex = {} if notempty(maxc) then c=maxc end if fargs.autocol=='yes' or fargs.autocol=='y' then autocol=true end local colspacing = tonumber(fargs['col-spacing']) or 5 maxtpm = 1 seeds = true forceseeds = false boldwinner = bargs('boldwinner') or '' if bargs('seeds')=='y' or bargs('seeds')=='yes' then forceseeds=true end if bargs('seeds')=='n' or bargs('seeds')=='no' then seeds=false end if bargs('aggregate')=='y' or bargs('aggregate')=='yes' then aggregate=true end if bargs('autolegs')=='y' or bargs('autolegs')=='yes' then autolegs=true end if bargs('nowrap')=='y' or bargs('nowrap')=='yes' then nowrap=true end if bargs('paramstyle')=='numbered' then paramstyle = 'numbered' else paramstyle = 'indexed' end getCells getAltIndices assignParams matchGroups if (boldwinner=='yes' or boldwinner=='y' or boldwinner=='high') then boldWinner end getPaths getGroups for j=minc,c do		maxlegs[j] = rlegs[j] for i=1,r do			if notempty(entries[j][i]) then if notempty(entries[j][i]['legs']) then maxlegs[j] = math.max(rlegs[j],entries[j][i]['legs']) end if autolegs then local l=1 repeat l=l+1 until isempty(entries[j][i]['score']) or isempty(entries[j][i]['score'][l]) maxlegs[j] = math.max(maxlegs[j],l-1) end end end end local tbl = mw.html.create('table') :attr('cellpadding','0') :attr('cellspacing','0') :css('font-size','90%') :css('border-collapse','collapse') :css('margin','1em 2em 1em 1em') tbl:tag('tr'):css('visibility','collapse') tbl:tag('td'):css('width','1px') for j=minc,c do		if seeds then tbl:tag('td'):css('width',getWidth('seed','25px')) end tbl:tag('td'):css('width',getWidth('team','150px')) if maxlegs[j]==0 then tbl:tag('td'):css('width',getWidth('score','25px')) else for l=1,maxlegs[j] do				tbl:tag('td'):css('width',getWidth('score','25px')) end end if aggregate and maxlegs[j]>1 then tbl:tag('td'):css('width',getWidth('score','25px')) end if j~=c then if hascross[j] then tbl:tag('td'):css('width',colspacing..'px') tbl:tag('td'):css('width','10px') tbl:tag('td'):css('width',colspacing..'px') else tbl:tag('td'):css('width',colspacing..'px') tbl:tag('td'):css('width',colspacing..'px') end end end for i=1,r do		local row = tbl:tag('tr') row:tag('td'):css('height','11px') for j=minc,c do			insertEntry(row,j,i) if j~=c then insertPath(row,j,i) end end end return tostring(tbl) end

return p