This is the module sandbox page for Module:Build bracket ( diff). |
This Lua module is used on approximately 7,500 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes at Wikipedia talk:High-risk templates before implementing them. |
This module is rated as beta, and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected. |
This module is intended to be used to create and edit intricate sports brackets that cannot be made by Module:RoundN or Module:Team bracket. For example, additional headers (for double-elimination brackets; 3rd-, 5th-, 7th-place matches; etc), singular omitted matches (i.e., having a horizonal line instead of a match), and N-way brackets (N teams per match). The syntax is slightly more complicated than the aforementioned brackets, but simpler than using the standard wikitable code.
{{#invoke:Build bracket|main | rounds = | col1-headers = | col1-matches = | col2-headers = | col2-matches = ... | col1-col2-paths = | col2-col3-paths = ... }}
Parameter | Description | Default |
---|---|---|
rounds |
number of rounds (columns). | 1
|
autocol |
set to yes to automatically set set maximum round to display based on entries. |
no
|
rows |
manually set the number of rows. | Automatic |
teams-per-match |
enter the number of teams in each match. Use colm-teams-per-match to set individual columns |
2
|
colm-headers |
(optional) enter the row numbers where headers are desired in column m. Separate entries with , . Half integer values are allowed. |
Automatic |
colm-matches |
enter the row numbers where a match is desired in column m. Matches take up two rows by default. Separate entries with , . Half integer values are allowed. |
|
RDmh-hide |
set to yes to hide the (alpha) hth header and all matches beneath it in column m unless any of those entries are non empty. Useful for consolation matches. |
|
colm-colm+1-paths |
enter the starting and ending row numbers, separated by - , from columns m and m+1 where a path is desired. Separate entries with , . Half integer values are allowed. |
|
colm-colm+1-cross |
enter the row number where paths intersect from column m to m+1. | |
RDm-altname |
Alternate name for RDm (e.g., if |RD1-altname=first , then first-team1 can be used instead of RD1-team1 ). Use RDmh-altname for cells under header mh. |
|
text-altname |
Alternate name for RDm-textk (e.g., if |text-altname=details , then RDm-details1 can be used instead of RDm-text1 ). |
|
maxround |
final round to display. This parameter should be omitted unless it is less than the default value set by rounds . |
|
minround |
first round to display. | 1
|
height |
the amount of vertical visibility desired for the bracket. Creates a vertical scroll bar. Enter a number with units (e.g., 30em or 480px ). |
|
col-spacing |
the amount of horizontal space between rounds. Enter as a plain number (e.g., 10 for 10px). |
5
|
seed-width |
the width of the cells for seeds. Plain numbers are assumed to be in px units (e.g., 25 for 25px 2em for 2em) |
25
|
team-width |
the width of the cells for team names. Plain numbers are assumed to be in px units (e.g., 200 for 200px or 15em for 15em) |
150
|
score-width |
the width of the cells for scores. Plain numbers are assumed to be in px units (e.g., 25 or 25px 2em for 2em) |
25
|
agg-width |
the width of the cells for aggregate scores. score-width will change aggregate score widths unless this parameter is used. Plain numbers are assumed to be in px units (e.g., 25 or 25px 2em for 2em) |
25
|
seeds |
set to no to omit seeds in in all matches. set to yes to show seed cells to show for all matches. |
|
legs |
the number of legs for all rounds. Use RDm-legs to individually set columns. Use RDm-legsk to individually set teams. |
1
|
autolegs |
set to yes to automatically generate score cells per team. If legs or RDm-legs is used, autolegs will be set to no . |
no
|
byes |
set to yes to hide any team cells that are empty. Alternatively, set to m to have rounds 1 through m hide any team cells that are empty. Use RDm-byes for just matches under column m. Use RDmh-byes for just matches under header h only in column m. |
no
|
show-bye-paths |
set to yes to replace any team cells that that are hidden byes with a path. |
no
|
aggregate |
set to yes to add an aggregate score box to each match. Only matches with two or more legs will show the aggregate score box. |
no
|
boldwinner |
set to yes to automatically bold the seed/team/score with the higher score in each match. |
no
|
shift |
vertically shifts all of the entries by the number entered. Use RDm-shift for individual in columns. |
0
|
RDm , RDmh |
The header text of the (alpha hth) header in column m (e.g., RD1 or RD1a for the first header and RD1b for the second header in column 1). |
|
RDm-seedk |
The seed of the kth team in column m. Alternatively, use RDmh-seedk for the kth team under header mh. |
|
RDm-teamk |
The name of the kth team in column m. Alternatively, use RDmh-teamk for the kth team under header mh. |
|
RDm-scorek |
The score of the kth team in column m. Alternatively, use RDmh-scorek for the kth team under header mh. Append the suffix -l for the lth leg or -agg for the aggregate score. |
|
RDm-textk |
The text above the kth match in column m. Alternatively, use RDmh-textk for the kth match under header mh. |
|
RDm-groupk |
The text for the kth group in column m. Group text will appear to the left of whenever two paths meet. | |
RD-shade |
the background color (in hex format, e.g. #ABCDEF ) of all headers. Use RDm-shade or RDmh-shade for individual headers. |
#F2F2F2
|
RDm-RD(m+1)-path
[a] |
set to no or 0 to omit the paths from round m to round m+1. |
yes
|
paramstyle
[b] |
set to numbered change the parameter name style of RDm-textk , RDm-seedk , RDm-teamk , and RDm-scorek to a numbered notation (|1= ,|2= ...). Set |seeds=yes add seeds. |
indexed
|
|RD1-team3=
and |RD1b-team1=
can be used to assign the third team in the first column. By default, entries with subheader prefixes will override those without. In the below example, RD1b-team1
will override any value that has been set by RD1-team3
.Upper round | |||
Lower round | |||
RD1-team3 or RD1b-team1 | |||
|RD1-seed1=1
set. If ArticleX implemented {{NTeamBracket|RD1-seed1=2}}
, then the first team in round 1 would have a seed of 2.Path codes are entered in the form a-b
, where a
is associated match in the first column, and b
is associated match in the second column. Path codes can be grouped; for example, (a,b)-c
is equivalent to a-c, b-c
. To add color, append :color
to the end of a path, e.g. 3-5:red
. Only one extra color can be used in a bracket.
Example | Output | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1-3
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(1,5)-3
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3-1,3-5:red
|
Note: These are only examples to illustrate parameters. Standard 4-team, 8-team, etc. brackets are better handled by Module:Team bracket.
{{#invoke:Build bracket|main | rounds=2 | col1-headers = 1 | col2-headers = 1 | col1-matches = 3,7 | col2-matches = 5 | col1-col2-paths = (3,7)-5 <!-- Defaults --> | RD2 = Grand Final | RD1-seed1 = 1 | RD1-seed3 = 2 }}
Semifinals | Grand Final | |||||||
1 | ||||||||
2 | ||||||||
{{#invoke:Build bracket|main | rounds=2 | teams-per-match = 3 | col1-headers = 1 | col2-headers = 1 | col1-matches = 3,7,11 | col2-matches = 7 | col1-col2-paths = (3,7,11)-7 }}
Semifinals | Final | |||||||
{{#invoke:Build bracket|main | rounds=4 | col1-headers = 1,7 | col2-headers = 1,7 | col3-headers = 7 | col4-headers = 1 | col1-matches = 4,11 | col2-matches = 3,10 | col3-matches = 9 | col4-matches = 6 | col1-col2-paths = 4-3, 11-10 | col2-col3-paths = 3-3, 10-9 | col3-col4-paths = (3,9)-6 <!-- Defaults --> | RD1 = Upper round 1 | RD2 = Upper final | RD3b = Lower final }}
Upper round 1 | Upper final | Final | ||||||||||||||||
Lower round 1 | Lower round 2 | Lower final | ||||||||||||||||
{{#invoke:Build bracket|main | rounds=2 | col1-matches = 3,7 | col2-matches = 3,7 | col1-col2-paths = 3-7, 7-3 | col1-col2-cross = 5 }}
Semifinals | Final | ||||||||
{{#invoke:Build bracket|main | rounds=2 | col1-matches = 3,6 | col2-matches = 4.5 | col1-col2-paths = (3,6)-4.5 | RD1-text1 = Text 1 | RD1-text2 = Text 2 | RD2-text1 = Text 3 }}
Semifinals | Final | |||||||
Text 1 | ||||||||
Text 3 | ||||||||
Text 2 | ||||||||
{{#invoke:Build bracket|main | rounds=3 | col1-matches = 3,6,9,12 | col2-matches = 4.5,10.5 | col3-matches = 7.5 | col1-col2-paths = (3,6)-4.5, (9,12)-10.5 | col2-col3-paths = (4.5,10.5)-7.5 | RD1-group1 = Group 1 | RD1-group2 = Group 2 | RD2-group1 = Group 3 }}
Quarterfinals | Semifinals | Final | |||||||||||
Group 1 | |||||||||||||
Group 3 | |||||||||||||
Group 2 | |||||||||||||
{{#invoke:Build bracket|main | rounds=3 | legs = 2 | RD1-legs = 3 | aggregate = y | col1-matches = 3,6,9,12 | col2-matches = 4.5,10.5 | col3-matches = 7.5 | col1-col2-paths = (3,6)-4.5, (9,12)-10.5 | col2-col3-paths = (4.5,10.5)-7.5 }}
Quarterfinals | Semifinals | Final | ||||||||||||||||||
{{#invoke:Build bracket|main | rounds=3 | RD1-byes = y | col1-matches = 3,6,9,12 | col2-matches = 4.5,10.5 | col3-matches = 7.5 | col1-col2-paths = (3,6)-4.5, (9,12)-10.5 | col2-col3-paths = (4.5,10.5)-7.5 | RD1-team1 = Team 1 | RD1-team2 = Team 2 }}
Quarterfinals | Semifinals | Final | |||||||||||
Team 1 | |||||||||||||
Team 2 | |||||||||||||
{{#invoke:Build bracket|main | rounds=2 | col1-matches = 3,7 | col2-matches = 5 | col1-col2-paths = (3,7)-5 | paramstyle = numbered | seeds = yes | Text 1 | 1 | Team 1 | 5 | 4 | Team 2 | 11 | Text 2 | 2 | Team 3 | 6 | 3 | Team 4 | 3 | Text 3 | 4 | Team 2 | 2 | 2 | Team 3 | 1 }}
Semifinals | Final | |||||||
Text 1 | ||||||||
1 | Team 1 | 5 | ||||||
4 | Team 2 | 11 | Text 3 | |||||
4 | Team 2 | 2 | ||||||
Text 2 | 2 | Team 3 | 1 | |||||
2 | Team 3 | 6 | ||||||
3 | Team 4 | 3 | ||||||
{{#invoke:Build bracket|main | rounds=2 | legs = 3 | boldwinner=y | col1-matches = 3,7 | col2-matches = 5 | col1-col2-paths = (3,7)-5 | RD1-seed1 = 1 | RD1-team1 = Team 1 | RD1-score1-1 = 5 | RD1-score1-2 = 12 | RD1-score1-3 = 15 | RD1-seed2 = 4 | RD1-team2 = Team 2 | RD1-score2-1 = 11 | RD1-score2-2 = 10 | RD1-score2-3 = 4 | RD1-seed3 = 2 | RD1-team3 = Team 3 | RD1-score3-1 = 6 | RD1-score3-2 = 13 | RD1-score3-3 = {{ndash}} | RD1-seed4 = 3 | RD1-team4 = Team 4 | RD1-score4-1 = 3 | RD1-score4-2 = 2 | RD1-score4-3 = {{ndash}} | RD2-seed1 = 4 | RD2-team1 = Team 2 | RD2-score1-1 = 2 | RD2-score1-2 = 2 | RD2-score1-3 = 5 | RD2-seed2 = 2 | RD2-team2 = Team 3 | RD2-score2-1 = 1 | RD2-score2-2 = 7 | RD2-score2-3 = 2 }}
Semifinals | Final | |||||||||||
1 | Team 1 | 5 | 12 | 15 | ||||||||
4 | Team 2 | 11 | 10 | 4 | ||||||||
4 | Team 2 | 2 | 2 | 5 | ||||||||
2 | Team 3 | 1 | 7 | 2 | ||||||||
2 | Team 3 | 6 | 13 | – | ||||||||
3 | Team 4 | 3 | 2 | – | ||||||||
local p = {}
local entries = {}
local pathCell = {}
local crossCell = {}
local skipPath = {}
local shift = {}
local hascross = {}
local teams_per_match = {}
local rlegs = {}
local maxlegs = {}
local autolegs
local byes = {}
local hide = {}
local matchgroup = {}
local nowrap
local autocol
local seeds
local forceseeds
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 pargss or fargss
end
local function toChar(num)
return string.char(string.byte("a")+num-1)
end
local function unboldParenthetical(text)
-- Replace wikilinks with unique placeholders
local counter = 0
local placeholders = {}
text = text:gsub('%[%[(.-)%]%]', function(link)
counter = counter + 1
local placeholder = '__WIKILINK__' .. counter .. '__'
placeholdersplaceholder = link
return placeholder
end)
-- Apply <span style="font-weight:normal"></span> to parenthetical and bracketed text
text = text:gsub('(%b())', '<span style="font-weight:normal">%1</span>')
:gsub('(%b[])', '<span style="font-weight:normal">%1</span>')
-- Restore the original wikilinks
for placeholder, link in pairs(placeholders) do
text = text:gsub(placeholder, '[[' .. link .. ']]')
end
return text
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
matchgroupj={}
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
matchgroupj][i=math.ceil(entriesj][i]['index'/teams_per_matchj])
entriesj][i]['group' = math.ceil(entriesj][i]['index'/teams_per_matchj])
end
end
end
end
local function teamLegs(j,i)
local legs = rlegsj
if notempty(entriesj][i]['legs']) then
legs = tonumber(entriesj][i]['legs'])
end
if autolegs then
local l=1
repeat l=l+1
until isempty(entriesj][i]['score'][l])
legs = l-1
end
return legs
end
local function boldWinner()
local function boldScore(j,i,l)
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
local myscore = entriesj][i]['score'][l]:gsub('%W','')
if myscore == "" or myscore:find("%D") then return 'normal'
else myscore=tonumber(myscore) end
local compscore = {}
for k,v in pairs(matchgroupj]) do
if matchgroupj][i==v and k~=i then
local theirscore = entriesj][k]['score'][l or ''
theirscore = theirscore:gsub('%W','')
if theirscore== "" or theirscore:find("%D") then return 'normal'
else table.insert(compscore,tonumber(theirscore)) end
end
end
for k,v in pairs(compscore) do
if myscore<=v then return 'normal' end
end
if l~='agg' then
entriesj][i]['wins' = entriesj][i]['wins'+1
else
entriesj][i]['aggwins' = 1
end
return 'bold'
end
end
local function boldTeam(j,i,agg)
local wins
local legs = teamLegs(j,i)
if agg~=true then
wins = 'wins'
if entriesj][i][wins>legs/2 then
return 'bold'
end
if autolegs then
for l=1,legs do
if notempty(entriesj][i]['score'][l]) and string.find(entriesj][i]['score'][l],"nbsp") then
return 'normal'
end
end
else
for l=1,legs do
if isempty(entriesj][i]['score'][l]) or string.find(entriesj][i]['score'][l],"nbsp") then
return 'normal'
end
end
end
else
wins = 'aggwins'
end
local compteam = {}
for k,v in pairs(matchgroupj]) do
if matchgroupj][i==v and k~=i then
table.insert(compteam,tonumber(entriesj][k][wins]))
end
end
for k,v in pairs(compteam) do
if entriesj][i][wins<=v then
return 'normal'
end
end
return 'bold'
end
for j=minc,c do
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
entriesj][i]['wins' = 0
entriesj][i]['aggwins' = 0
end
end
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
local legs = teamLegs(j,i)
for l=1,legs do
entriesj][i]['score']['weight'][l = boldScore(j,i,l)
end
if aggregate and legs>1 then
entriesj][i]['score']['weight']['agg' = boldScore(j,i,'agg')
end
end
end
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
local agg
local legs = teamLegs(j,i)
if aggregate and legs>1 then agg=true end
entriesj][i]['weight' = boldTeam(j,i,agg)
end
end
end
end
local function isBlankEntry(col,row,ctype)
if isempty(entriescol][row]) then return true end
if isempty(entriescol][row]['team']) and isempty(entriescol][row]['text']) then return true end
return false
end
local function showSeeds(j,i)
local showseed=false
if forceseeds or notempty(entriesj][i]['seed']) then
showseed=true
else
for k=1,teams_per_matchj-1 do
if notempty(entriesj][i+2*k]) and entriesj][i]['group'==entriesj][i+2*k]['group' and notempty(entriesj][i+2*k]['seed']) then
showseed=true
end
if notempty(entriesj][i-2*k]) and entriesj][i]['group'==entriesj][i-2*k]['group' and notempty(entriesj][i-2*k]['seed']) then
showseed=true
end
end
end
return showseed
end
local function cellBorder(b)
return b1..'px '..b2..'px '..b3..'px '..b4..'px'
end
local function Cell(tbl,j,i,rowspan,colspan,text,align,border,border_width,bg,padding,weight,nwrap)
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(border) then
cell:css('border',border)
end
if notempty(border_width) then
cell:css('border-width',cellBorder(border_width))
end
if notempty(bg) then
cell:css('background-color',bg)
end
if notempty(align) then
cell:css('text-align',align)
end
cell:css('padding','0em 0.3em')
if weight=='bold' then
cell:css('font-weight',weight)
end
if notempty(text) then
cell:wikitext(text)
end
return cell
end
local function teamCell(tbl,k,j,i,l,colspan)
local bg = '#F2F2F2'
local align
local padding
local weight
local text
local nwrap
local b={0,0,1,1}
if k=='seed' or k=='score' then
align='center'
end
if k~='seed' then
bg='#F9F9F9'
end
if k=='team' then
padding='0.3em'
if teamLegs(j,i)==0 then
b2=1
end
end
if entriesj][i]['position'=='top' then
b1=1
end
if l==teamLegs(j,i) or l=='agg' or k=='seed' then
b2=1
end
if (l==nil and entriesj][i]['weight'=='bold') or entriesj][i]['score']['weight'][l=='bold' then
weight='bold'
end
if l==nil then
text=unboldParenthetical(entriesj][i][k])
else
text=tostring(entriesj][i][k][l])
end
return Cell(tbl,j,i,2,colspan,text,align,'solid #aaa',b,bg,padding,weight,nwrap)
end
local function insertEntry(tbl,j,i)
local entry_colspan=maxlegsj+2
if not seeds then entry_colspan=entry_colspan-1 end
if (aggregate and maxlegsj>1) or maxlegsj==0 then
entry_colspan=entry_colspan+1
end
if entriesj][i~=nil and entriesj][i]['ctype'=='blank' then
return
end
if entriesj][i==nil then
if entriesj][i-1~=nil or i==1 then
local rowspan = 0
local row = i
repeat
rowspan=rowspan+1
row=row+1
until entriesj][row~=nil or row>r
return Cell(tbl,j,i,rowspan,entry_colspan)
else
return
end
end
if entriesj][i]['ctype'=='header' then
if byesj][entriesj][i]['headerindex']] then
local emptyround = true
local row = i+1
repeat
if not isBlankEntry(j,row) then
emptyround = false
end
row = row+1
until (entriesj][row~=nil and entriesj][row]['ctype'=='header') or row>r
if emptyround == true then
return Cell(tbl,j,i,2,entry_colspan)
end
end
if hidej][entriesj][i]['headerindex']] then
return Cell(tbl,j,i,2,entry_colspan)
end
if isempty(entriesj][i]['header']) then
if entriesj][i]['headerindex'==1 then
if j==c then entriesj][i]['header' = 'Final'
elseif j==c-1 then entriesj][i]['header' = 'Semifinals'
elseif j==c-2 then entriesj][i]['header' = 'Quarterfinals'
else entriesj][i]['header' = 'Round '..j
end
else
entriesj][i]['header' = 'Lower round '..j
end
end
return Cell(tbl,j,i,2,entry_colspan,entriesj][i]['header'],'center','1px solid #aaa',nil,entriesj][i]['shade'])
end
if entriesj][i]['ctype'=='team' then
if (byesj][entriesj][i]['headerindex']] and isBlankEntry(j,i)) or hidej][entriesj][i]['headerindex']] then
return Cell(tbl,j,i,2,entry_colspan)
end
local legs = teamLegs(j,i)
local team_colspan = maxlegsj-legs+1
if aggregate and legs==1 and maxlegsj>1 then
team_colspan=team_colspan+1
end
if maxlegsj==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 entriesj][i]['ctype'=='text' then
Cell(tbl,j,i,2,entry_colspan,entriesj][i]['text'],nil,nil,nil,nil,'0.3em')
end
if entriesj][i]['ctype'=='group' then
local colspan=0
for m=j,entriesj][i]['colspan'+j-1 do
colspan=colspan+maxlegsm+2
if not seeds then colspan=colspan-1 end
if (aggregate and maxlegsm>1) or maxlegsm==0 then
colspan=colspan+1
end
end
colspan = colspan+2*(entriesj][i]['colspan'-1)
return Cell(tbl,j,i,2,colspan,entriesj][i]['group'],'center')
end
if entriesj][i]['ctype'=='line' then
local b={0,0,0,0}
b3=2*pathCellj-1][i+1][3][1][3
return Cell(tbl,j,i,2,entry_colspan,entriesj][i]['text'],nil,'solid black',b)
end
if entriesj][i]['ctype'=='line2' then
local b={0,0,0,0}
b1=2*pathCellj-1][i][3][1][1
return Cell(tbl,j,i,2,entry_colspan,entriesj][i]['text'],nil,'solid black',b)
end
end
local function isRoundHidden(j,i,headerindex)
if notempty(entriesj][i]['pheader']) then
hidej][entriesj][i]['headerindex']] = false
end
local row = i+1
repeat
if not isBlankEntry(j,row) then
hidej][entriesj][i]['headerindex']] = false
end
row = row+1
until (entriesj][row~=nil and entriesj][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(entriesj][i]['headerindex']),bargs('RD'..j..toChar(entriesj][i]['headerindex'])..'-altname') or 'RD'..j..toChar(entriesj][i]['headerindex'])}
}
local name = {cname, bargs(cname..'-altname') or cname}
local index = {entriesj][i]['index'], entriesj][i]['altindex']}
local result = {}
if cname=='header' then
if entriesj][i]['headerindex'==1 then
for k=1,2 do
table.insert(result,bargs(rname1][3-k]) or '')
table.insert(result,bargs(rname2][3-k]) or '')
end
else
for k=1,2 do
table.insert(result,bargs(rname2][3-k]) or '')
end
end
elseif cname=='pheader' then
if entriesj][i]['headerindex'==1 then
for k=1,2 do
table.insert(result,pargsrname1][3-k]] or '')
table.insert(result,pargsrname2][3-k]] or '')
end
else
for k=1,2 do
table.insert(result,pargsrname2][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(rname3-m][3-k..'-'..name1..index3-m]) or bargs(rname3-m][3-k..'-'..name1..'0'..index3-m]) or '')
end
table.insert(result,bargs(rname3-m][3-k..'-'..name1..index3-m..'-'..l) or bargs(rname3-m][3-k..'-'..name1..'0'..index3-m..'-'..l) or '')
end end
elseif cname=='shade' then
for k=1,2 do
if entriesj][i]['headerindex'==1 then
table.insert(result,bargs(rname1][3-k..'-'..name1]) or '')
else
table.insert(result,bargs(rname2][3-k..'-'..name1]) 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(rname3-m][3-k..'-'..name3-n..index3-m]) or bargs(rname3-m][3-k..'-'..name3-n..'0'..index3-m]) or '')
end end end
else
for m=1,2 do for k=1,2 do
table.insert(result,bargs(rname3-m][3-k..'-'..name1..index3-m]) or bargs(rname3-m][3-k..'-'..name1..'0'..index3-m]) or '')
end end
end
for k=1,#result do
if notempty(resultk]) then
return resultk
end
end
return ''
end
local function indexedParams(j)
for i=1,r do
if entriesj][i~=nil then
if entriesj][i]['ctype'=='team' then
local legs = rlegsj
if forceseeds then
entriesj][i]['seed' = bargs(masterindex) or ''
masterindex = masterindex+1
end
entriesj][i]['team' = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
entriesj][i]['legs' = paramNames('legs',j,i)
entriesj][i]['score' = {}
entriesj][i]['weight' = 'normal'
entriesj][i]['score']['weight' = {}
if notempty(entriesj][i]['legs']) then
legs = tonumber(entriesj][i]['legs'])
end
for l=1,legs do
entriesj][i]['score'][l = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
entriesj][i]['score']['weight'][l = 'normal'
end
if aggregate and legs>1 then
entriesj][i]['score']['agg' = bargs(masterindex) or ''
masterindex = masterindex+1
entriesj][i]['score']['weight']['agg' = 'normal'
end
end
if entriesj][i]['ctype'=='header' then
entriesj][i]['header' = paramNames('header',j,i)
entriesj][i]['pheader' = paramNames('pheader',j,i)
entriesj][i]['shade' = paramNames('shade',j,i)
end
if entriesj][i]['ctype'=='text' then
entriesj][i]['text' = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
end
if entriesj][i]['ctype'=='group' then
entriesj][i]['group' = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
end
if entriesj][i]['ctype' == 'line' and entriesj][i]['hastext'==true then
entriesj][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
rlegsj = 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 entriesj][i~=nil then
if entriesj][i]['ctype'=='team' then
local legs = rlegsj
entriesj][i]['seed' = paramNames('seed',j,i)
entriesj][i]['team' = paramNames('team',j,i)
entriesj][i]['legs' = paramNames('legs',j,i)
entriesj][i]['score' = {}
entriesj][i]['weight' = 'normal'
entriesj][i]['score']['weight' = {}
if notempty(entriesj][i]['legs']) then
legs = tonumber(entriesj][i]['legs'])
end
if autolegs then
local l=1
repeat
entriesj][i]['score'][l = paramNames('score',j,i,l)
entriesj][i]['score']['weight'][l = 'normal'
l=l+1
until isempty(paramNames('score',j,i,l))
legs = l-1
else
for l=1,legs do
entriesj][i]['score'][l = paramNames('score',j,i,l)
entriesj][i]['score']['weight'][l = 'normal'
end
end
if aggregate and legs>1 then
entriesj][i]['score']['agg' = paramNames('score',j,i,'agg')
entriesj][i]['score']['weight']['agg' = 'normal'
end
end
if entriesj][i]['ctype'=='header' then
entriesj][i]['header' = paramNames('header',j,i)
entriesj][i]['pheader' = paramNames('pheader',j,i)
entriesj][i]['shade' = paramNames('shade',j,i)
end
if entriesj][i]['ctype'=='text' then
entriesj][i]['text' = paramNames('text',j,i)
end
if entriesj][i]['ctype'=='group' then
entriesj][i]['group' = paramNames('group',j,i)
end
if entriesj][i]['ctype' == 'line' and entriesj][i]['hastext'==true then
entriesj][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 entriesj][i~=nil and entriesj][i]['ctype'=='header' then
isRoundHidden(j,i)
end
if entriesj][i~=nil and not hidej][entriesj][i]['headerindex']] then
if not byesj][entriesj][i]['headerindex']] or (byesj][entriesj][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,headerindexj do
if byesj][k or hidej][k then
r=byerows+1
end
end
end
if autocol then
c = maxcol
end
end
local function getHide(j,headerindex)
hidej = {}
for k=1,headerindexj do
if bargs('RD'..j..toChar(k)..'-hide')=='yes' or bargs('RD'..j..toChar(k)..'-hide')=='y' then
hidej][k=true
end
end
end
local function getByes(j,headerindex)
byesj = {}
for k=1,headerindexj do
if bargs('byes')=='yes' or bargs('byes')=='y' then
byesj][k=true
elseif tonumber(bargs('byes')) then
if j<=tonumber(bargs('byes')) then
byesj][k=true
end
else
byesj][k=false
end
if bargs('RD'..j..'-byes')=='yes' or bargs('RD'..j..'-byes')=='y' then
byesj][k=true
elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then
byesj][k=false
end
if bargs('RD'..j..toChar(k)..'-byes')=='yes' or bargs('RD'..j..toChar(k)..'-byes')=='y' then
byesj][k=true
elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then
byesj][k=false
end
end
end
local function getAltIndices()
local teamindex=1
local textindex=1
local groupindex=1
for j=minc,c do
headerindexj=0
for i=1,r do
if entriesj][i==nil and i==1 then
headerindexj=headerindexj+1
end
if entriesj][i~=nil then
if entriesj][i]['ctype' == 'header' then
entriesj][i]['altindex' = headerindexj
teamindex=1
textindex=1
headerindexj=headerindexj+1
elseif entriesj][i]['ctype' == 'team' then
entriesj][i]['altindex' = teamindex
teamindex=teamindex+1
elseif entriesj][i]['ctype' == 'text' then
entriesj][i]['altindex' = textindex
textindex=textindex+1
elseif entriesj][i]['ctype' == 'group' then
entriesj][i]['altindex' = groupindex
groupindex=groupindex+1
elseif entriesj][i]['ctype' == 'line' and entriesj][i]['hastext'==true then
entriesj][i]['altindex' = textindex
textindex=textindex+1
end
entriesj][i]['headerindex' = headerindexj
end
end
getByes(j,headerindex)
getHide(j,headerindex)
end
end
local function noPaths(j,i)
local result = true
local cols = 2
if hascrossj==true then
cols = 3
end
for k=1,cols do
for n=1,4 do
if pathCellj][i][k][1][n~=0 then
result = false
return result
end
end
end
if hascrossj==true and (crossCellj][i]['left'][1==1 or crossCellj][i]['right'][1==1) then
result = false
return result
end
return result
end
local function generatePathCell(tbl,j,i,k,bg,rowspan)
local color = pathCellj][i][k]['color'
if not hascrossj and k==2 then
return
end
local cell=tbl:tag('td')
local a=pathCellj][i
if rowspan~=1 then
cell:attr('rowspan',rowspan)
end
if notempty(bg) and k==2 then
cell:css('background',bg)
:css('transform','translate(-1px)')
end
if ak][1][1~=0 or ak][1][2~=0 or ak][1][3~=0 or ak][1][4~=0 then
cell:css('border','solid '..color)
:css('border-width',2*ak][1][1..'px '..2*ak][1][2..'px '..2*ak][1][3..'px '..2*ak][1][4..'px')
end
return cell
end
local function insertPath(tbl,j,i)
if skipPathj][i then
return
end
local colspan = 2
local rowspan = 1
local angle = 58.2
local pathcolor
local bg = ''
local cross = {'',''}
if i<r then
local function repeatedPath(a)
if a>r-1 or skipPathj][a then
return false
end
for k=1,3 do
for n=1,4 do
if pathCellj][i][k][1][n~=pathCellj][a][k][1][n then
return false
end
end
end
return true
end
if repeatedPath(i) then
local row=i
repeat
if row~=i and repeatedPath(row) then
skipPathj][row=true
end
rowspan=rowspan+1
row=row+1
until row>r or not repeatedPath(row)
rowspan=rowspan-1
end
end
if i>1 and (crossCellj][i-1]['left'][1==1 or crossCellj][i-1]['right'][1==1) then
return
end
if hascrossj then
colspan = 3
if crossCellj][i]['left'][1==1 or crossCellj][i]['right'][1==1 then
rowspan = 2
if crossCellj][i]['left'][1==1 then
cross1 = 'linear-gradient(to top right, transparent calc(50% - 1px),'..crossCellj][i]['left'][2..' calc(50% - 1px),'..crossCellj][i]['left'][2..' calc(50% + 1px), transparent calc(50% + 1px))'
end
if crossCellj][i]['right'][1==1 then
cross2 = 'linear-gradient(to bottom right, transparent calc(50% - 1px),'..crossCellj][i]['right'][2..' calc(50% - 1px),'..crossCellj][i]['right'][2..' calc(50% + 1px), transparent calc(50% + 1px))'
end
end
if notempty(cross1]) and notempty(cross2]) then
cross1 = cross1..','
end
bg = cross1..cross2
end
for k=1,3 do
generatePathCell(tbl,j,i,k,bg,rowspan)
end
end
local function parsePaths(j)
local result={}
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 array = split(val:gsub("%s+",""):gsub("%)",""):gsub("%(",""),{"-"})
for k,_ in pairs(array) do
arrayk = split(arrayk],{","})
end
if notempty(array2]) then
for m=1,#array2 do
array3 = {}
array2][m = split(array2][m],{":"})
array3][m = array2][m][2
array2][m = array2][m][1
end
for n=1,#array1 do
for m=1,#array2 do
table.insert(result,{tonumber(array1][n]),tonumber(array2][m]),['color'=array3][m]})
end
end
end
end
return result
end
local function isPathHidden(j,i,start,stop)
local result=false
if notempty(entriesj][start-1]) and (byesj][entriesj][start-1]['headerindex']] and isBlankEntry(j,start-1) and isBlankEntry(j,start+1) or hidej][entriesj][start-1]['headerindex']]) then
if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then
result=true
end
end
if notempty(entriesj+1][stop-1]) and (byesj+1][entriesj+1][stop-1]['headerindex']] and isBlankEntry(j+1,stop-1) and isBlankEntry(j+1,stop+1) or hidej+1][entriesj+1][stop-1]['headerindex']])then
if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then
result=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(entriesj][start-1]) and entriesj][start-1]['headerindex'==1 then
result=true
end
end
return result
end
local function getPaths()
local paths = {}
for j=minc,c-1 do
hascrossj = false
if notempty(fargs'col'..j..'-col'..(j+1)..'-cross']) then
hascrossj = true
end
end
for j=minc,c-1 do
local straightpaths = {}
local outpaths = {}
local inpaths = {}
pathsj=parsePaths(j)
pathCellj = {}
crossCellj = {}
skipPathj = {}
for i=1,r do
pathCellj][i = {}
crossCellj][i = {['left'={0,'black'},['right'={0,'black'}}
for k=1,3 do
pathCellj][i][k = {{0,0,0,0},['color'='black'}
end
skipPathj][i = false
end
local crossloc = split((fargs'col'..j..'-col'..(j+1)..'-cross' or ''):gsub("%s+", ""),{","},true)
if shiftj~=0 and notempty(crossloc1]) then
for n=1,#crossloc do
crosslocn = crosslocn+shiftj
end
end
for k,v in ipairs(pathsj]) do
local start = 2*(pathsj][k][1+shiftj])+(teams_per_matchj-2)
local stop = 2*(pathsj][k][2+shiftj+1])+(teams_per_matchj+1-2)
local mid = {}
local cross = 0
if notempty(crossloc1]) then
for n=1,#crossloc do
midn = 2*crosslocn+(teams_per_matchj-2)
end
else
mid1=0
end
for n=1,#mid do
if (start<stop and midn<stop and midn>start) or (start>stop and midn>stop and midn<start) then
cross = midn
end
end
pathsj][k]['color' = pathsj][k]['color' or 'black'
table.insert(outpaths,{start,pathsj][k]['color']})
table.insert(inpaths,{stop,pathsj][k]['color']})
if not isPathHidden(j,i,start,stop) then
if start==stop then
table.insert(straightpaths,{start,pathsj][k]['color']})
elseif start<stop then
if stop>r then break end
pathCellj][start+1][1][1][1 = 1
pathCellj][start+1][1]['color' = pathsj][k]['color'
if cross==0 then
if hascrossj then
pathCellj][start+1][2][1][1 = 1
pathCellj][start+1][2]['color' = pathsj][k]['color'
for i=start+1,stop do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
else
for i=start+1,stop do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
end
else
crossCellj][cross]['left' = {1,pathsj][k]['color']}
for i=start+1,cross-1 do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
for i=cross+2,stop do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
end
pathCellj][stop][3][1][3 = 1
pathCellj][stop][3]['color' = pathsj][k]['color'
elseif start>stop then
if start>r then break end
pathCellj][stop+1][3][1][1 = 1
pathCellj][stop+1][3]['color' = pathsj][k]['color'
if cross==0 then
if hascrossj then
for i=stop+1,start do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
pathCellj][start][2][1][3 = 1
pathCellj][start][2]['color' = pathsj][k]['color'
else
for i=stop+1,start do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
end
else
crossCellj][cross]['right' = {1,pathsj][k]['color']}
for i=stop+1,cross-1 do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
for i=cross+2,start do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
end
pathCellj][start][1][1][3 = 1
pathCellj][start][1]['color' = pathsj][k]['color'
end
end
end
-- Thicken start==stop paths
for n=1,#straightpaths do
local i = straightpathsn][1
local color = straightpathsn][2
if i>r then break end
if pathCellj][i][1][1][3==0 then
pathCellj][i][1][1][3 = 1
pathCellj][i][2][1][3 = 1
pathCellj][i][3][1][3 = 1
pathCellj][i][1]['color' = color
pathCellj][i][2]['color' = color
pathCellj][i][3]['color' = color
if pathCellj][i+1][1][1][1==0 then
pathCellj][i+1][1][1][1 = 0.5
pathCellj][i+1][2][1][1 = 0.5
pathCellj][i+1][3][1][1 = 0.5
pathCellj][i+1][1]['color' = color
pathCellj][i+1][2]['color' = color
pathCellj][i+1][3]['color' = color
end
elseif pathCellj][i+1][1][1][1==0 then
pathCellj][i+1][1][1][1 = 1
pathCellj][i+1][1]['color' = color
if hascrossj then
pathCellj][i+1][2][1][1 = 1
pathCellj][i+1][2]['color' = color
end
pathCellj][i+1][3][1][1 = 1
pathCellj][i+1][3]['color' = color
end
end
-- Thicken/Thin out paths
for n=1,#outpaths do
local i = outpathsn][1
local color = outpathsn][2
if i<r and pathCellj][i][1][1][3==1 and pathCellj][i+1][1][1][1==0 then
pathCellj][i+1][1][1][1 = 0.5*pathCellj][i][1][1][3
pathCellj][i+1][2][1][1 = 0.5*pathCellj][i][2][1][3
pathCellj][i+1][1]['color' = pathCellj][i][1]['color'
pathCellj][i+1][2]['color' = pathCellj][i][2]['color'
elseif i<r and pathCellj][i][1][1][3==0 and pathCellj][i+1][1][1][1==1 then
pathCellj][i][1][1][3 = pathCellj][i+1][1][1][1
pathCellj][i][2][1][3 = pathCellj][i+1][2][1][1
pathCellj][i+1][1][1][1 = 0.5*pathCellj][i][1][1][3
pathCellj][i+1][2][1][1 = 0.5*pathCellj][i][2][1][3
pathCellj][i][1]['color' = pathCellj][i+1][1]['color'
pathCellj][i][2]['color' = pathCellj][i+1][2]['color'
end
end
-- Thin double-in paths
for n=1,#inpaths do
local i = inpathsn][1
local color = inpathsn][2
if i<r and pathCellj][i][3][1][3==1 and pathCellj][i+1][3][1][1==1 and pathCellj][i][3]['color'==pathCellj][i+1][3]['color' then
pathCellj][i+1][3][1][1 = 0.5*pathCellj][i][3][1][3
end
end
end
for j=minc,c-1 do
for i=1,r-1 do
local straightpath=false
if (entriesj+1][i-1==nil or (byesj+1][entriesj+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) then
if (pathCellj][i][3][1][3~=0 and pathCellj+1][i][1][1][3~=0) or (pathCellj][i+1][3][1][1~=0 and pathCellj+1][i+1][1][1][1~=0) then
if pathCellj+1][i][1][1][3==pathCellj+1][i][3][1][3 and pathCellj+1][i+1][1][1][1==pathCellj+1][i+1][3][1][1 then
straightpath=true
end
pathCellj+1][i][1][1][3=pathCellj][i][3][1][3
pathCellj+1][i+1][1][1][1=pathCellj][i+1][3][1][1
pathCellj+1][i][2][1][3=pathCellj][i][3][1][3
pathCellj+1][i+1][2][1][1=pathCellj][i+1][3][1][1
entriesj+1][i-1={['ctype'='line'}
entriesj+1][i={['ctype'='blank'}
if notempty(entriesj+1][i+1]) then
entriesj+1][i+1]['ctype' = 'line2'
else
entriesj+1][i+1={['ctype'='line2'}
end
entriesj+1][i+2={['ctype'='blank'}
if straightpath then
pathCellj+1][i][3][1][3=pathCellj+1][i][1][1][3
pathCellj+1][i+1][3][1][1=pathCellj+1][i+1][1][1][1
end
end
end
end
end
end
local function getGroups()
local function check(j,i)
local result=false
if entriesj][i == nil then
if entriesj][i+1 == nil then
result=true
elseif entriesj][i+1]['ctype'=='text' and isBlankEntry(j,i+1) then
result=true
end
elseif entriesj][i]['ctype'=='text' and isBlankEntry(j,i) then
result=true
end
return result
end
for j=minc,c-1 do
if teams_per_matchj==2 then
local n=0
for i=1,r-1 do
if pathCellj][i][3][1][3==1 or pathCellj][i+1][3][1][1==1 then
n=n+1
if check(j,i) then
local k=minc-1
repeat
if entriesj-k][i+1~=nil and entriesj-k][i+1]['ctype'=='text' and isBlankEntry(j-k,i+1) then
entriesj-k][i+2=nil
end
entriesj-k][i={['ctype'='blank'}
entriesj-k][i+1={['ctype'='blank'}
if k>0 and noPaths(j-k,i) then
skipPathj-k][i = true
skipPathj-k][i+1 = true
end
k=k+1
until k>j-1 or not check(j-k,i) or not noPaths(j-k,i)
k=k-1
entriesj-k][i={['ctype'='group',['index'=n,['colspan'=k+1}
entriesj-k][i+1={['ctype'='blank'}
entriesj-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_matchj = 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_matchj])
end
for j=minc,c do
entriesj = {}
shiftj = tonumber(bargs('RD'..j..'-shift')) or tonumber(bargs('shift')) or 0
colentryj = {
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(colentryj][1],1)
end
end
for j=minc,c do
local textindex=0
for k,v in ipairs(colentryj]) do
table.sort(colentryj][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,#colentryj][k do
if shiftj~=0 and colentryj][k][n>1 then
colentryj][k][n = colentryj][k][n+shiftj
end
local i=2*colentryj][k][n-1
maxrow = math.max(i+2*teams_per_matchj-1,maxrow)
if ctype=='team' then
if entriesj][i-1==nil and entriesj][i-2==nil then
entriesj][i-2={['ctype'='text',['index'=n}
entriesj][i-1={['ctype'='blank'}
textindex=n
end
entriesj][i={['ctype'=ctype,['index'=teams_per_matchj*n-(teams_per_matchj-1),['position'='top'}
entriesj][i+1={['ctype'='blank'}
for m=2,teams_per_matchj do
entriesj][i+2*(m-1)]={['ctype'=ctype,['index'=teams_per_matchj*n-(teams_per_matchj-m)}
entriesj][i+2*(m-1)+1={['ctype'='blank'}
end
elseif ctype=='text' then
entriesj][i={['ctype'=ctype,['index'=textindex+n}
entriesj][i+1={['ctype'='blank'}
elseif ctype=='line' then
entriesj][i={['ctype'=ctype}
entriesj][i+1={['ctype'='blank'}
entriesj][i+2={['ctype'='line2'}
entriesj][i+3={['ctype'='blank'}
elseif ctype=='group' then
entriesj][i={['ctype'=ctype,['index'=n}
entriesj][i+1={['ctype'='blank'}
else
entriesj][i={['ctype'=ctype,['index'=n,['position'='top'}
entriesj][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
local height = bargs('height') or 0
maxtpm = 1
seeds = true
nowrap = 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('paramstyle')=='numbered' then
paramstyle = 'numbered'
else
paramstyle = 'indexed'
end
if pargs.nowrap=='n' or pargs.nowrap=='no' then nowrap=false end
getCells()
getAltIndices()
assignParams()
matchGroups()
if (boldwinner=='yes' or boldwinner=='y' or boldwinner=='high') then boldWinner() end
getPaths()
if minc==1 then
getGroups()
end
for j=minc,c do
maxlegsj = rlegsj
for i=1,r do
if notempty(entriesj][i]) then
if notempty(entriesj][i]['legs']) then
maxlegsj = math.max(rlegsj],entriesj][i]['legs'])
end
if autolegs then
local l=1
repeat l=l+1
until isempty(entriesj][i]['score']) or isempty(entriesj][i]['score'][l])
maxlegsj = math.max(maxlegsj],l-1)
end
end
end
end
local div = mw.html.create('div')
:css('overflow','auto')
if height~=0 then
div:css('height',height)
end
local tbl = mw.html.create('table')
:attr('cellpadding','0')
:attr('cellspacing','0')
:css('font-size','90%')
:css('border-collapse','separate')
:css('margin','1em 2em 0em 1em')
if nowrap then
tbl:css('white-space','nowrap')
end
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 maxlegsj==0 then
tbl:tag('td'):css('width',getWidth('score','25px'))
else
for l=1,maxlegsj do
tbl:tag('td'):css('width',getWidth('score','25px'))
end
end
if aggregate and maxlegsj>1 then
tbl:tag('td'):css('width',getWidth('agg',getWidth('score','25px')))
end
if j~=c then
if hascrossj then
tbl:tag('td'):css('width',colspacing+1-4 ..'px')
:css('padding-left','4px')
tbl:tag('td'):css('padding-left','5px')
:css('width','5px')
tbl:tag('td'):css('width',colspacing-1-2 ..'px')
:css('padding-right','2px')
else
tbl:tag('td'):css('width',colspacing+1-4 ..'px')
:css('padding-left','4px')
tbl:tag('td'):css('width',colspacing-1-2 ..'px')
:css('padding-right','2px')
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
div:wikitext(tostring(tbl))
return tostring(div)
end
return p
This is the module sandbox page for Module:Build bracket ( diff). |
This Lua module is used on approximately 7,500 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes at Wikipedia talk:High-risk templates before implementing them. |
This module is rated as beta, and is ready for widespread use. It is still new and should be used with some caution to ensure the results are as expected. |
This module is intended to be used to create and edit intricate sports brackets that cannot be made by Module:RoundN or Module:Team bracket. For example, additional headers (for double-elimination brackets; 3rd-, 5th-, 7th-place matches; etc), singular omitted matches (i.e., having a horizonal line instead of a match), and N-way brackets (N teams per match). The syntax is slightly more complicated than the aforementioned brackets, but simpler than using the standard wikitable code.
{{#invoke:Build bracket|main | rounds = | col1-headers = | col1-matches = | col2-headers = | col2-matches = ... | col1-col2-paths = | col2-col3-paths = ... }}
Parameter | Description | Default |
---|---|---|
rounds |
number of rounds (columns). | 1
|
autocol |
set to yes to automatically set set maximum round to display based on entries. |
no
|
rows |
manually set the number of rows. | Automatic |
teams-per-match |
enter the number of teams in each match. Use colm-teams-per-match to set individual columns |
2
|
colm-headers |
(optional) enter the row numbers where headers are desired in column m. Separate entries with , . Half integer values are allowed. |
Automatic |
colm-matches |
enter the row numbers where a match is desired in column m. Matches take up two rows by default. Separate entries with , . Half integer values are allowed. |
|
RDmh-hide |
set to yes to hide the (alpha) hth header and all matches beneath it in column m unless any of those entries are non empty. Useful for consolation matches. |
|
colm-colm+1-paths |
enter the starting and ending row numbers, separated by - , from columns m and m+1 where a path is desired. Separate entries with , . Half integer values are allowed. |
|
colm-colm+1-cross |
enter the row number where paths intersect from column m to m+1. | |
RDm-altname |
Alternate name for RDm (e.g., if |RD1-altname=first , then first-team1 can be used instead of RD1-team1 ). Use RDmh-altname for cells under header mh. |
|
text-altname |
Alternate name for RDm-textk (e.g., if |text-altname=details , then RDm-details1 can be used instead of RDm-text1 ). |
|
maxround |
final round to display. This parameter should be omitted unless it is less than the default value set by rounds . |
|
minround |
first round to display. | 1
|
height |
the amount of vertical visibility desired for the bracket. Creates a vertical scroll bar. Enter a number with units (e.g., 30em or 480px ). |
|
col-spacing |
the amount of horizontal space between rounds. Enter as a plain number (e.g., 10 for 10px). |
5
|
seed-width |
the width of the cells for seeds. Plain numbers are assumed to be in px units (e.g., 25 for 25px 2em for 2em) |
25
|
team-width |
the width of the cells for team names. Plain numbers are assumed to be in px units (e.g., 200 for 200px or 15em for 15em) |
150
|
score-width |
the width of the cells for scores. Plain numbers are assumed to be in px units (e.g., 25 or 25px 2em for 2em) |
25
|
agg-width |
the width of the cells for aggregate scores. score-width will change aggregate score widths unless this parameter is used. Plain numbers are assumed to be in px units (e.g., 25 or 25px 2em for 2em) |
25
|
seeds |
set to no to omit seeds in in all matches. set to yes to show seed cells to show for all matches. |
|
legs |
the number of legs for all rounds. Use RDm-legs to individually set columns. Use RDm-legsk to individually set teams. |
1
|
autolegs |
set to yes to automatically generate score cells per team. If legs or RDm-legs is used, autolegs will be set to no . |
no
|
byes |
set to yes to hide any team cells that are empty. Alternatively, set to m to have rounds 1 through m hide any team cells that are empty. Use RDm-byes for just matches under column m. Use RDmh-byes for just matches under header h only in column m. |
no
|
show-bye-paths |
set to yes to replace any team cells that that are hidden byes with a path. |
no
|
aggregate |
set to yes to add an aggregate score box to each match. Only matches with two or more legs will show the aggregate score box. |
no
|
boldwinner |
set to yes to automatically bold the seed/team/score with the higher score in each match. |
no
|
shift |
vertically shifts all of the entries by the number entered. Use RDm-shift for individual in columns. |
0
|
RDm , RDmh |
The header text of the (alpha hth) header in column m (e.g., RD1 or RD1a for the first header and RD1b for the second header in column 1). |
|
RDm-seedk |
The seed of the kth team in column m. Alternatively, use RDmh-seedk for the kth team under header mh. |
|
RDm-teamk |
The name of the kth team in column m. Alternatively, use RDmh-teamk for the kth team under header mh. |
|
RDm-scorek |
The score of the kth team in column m. Alternatively, use RDmh-scorek for the kth team under header mh. Append the suffix -l for the lth leg or -agg for the aggregate score. |
|
RDm-textk |
The text above the kth match in column m. Alternatively, use RDmh-textk for the kth match under header mh. |
|
RDm-groupk |
The text for the kth group in column m. Group text will appear to the left of whenever two paths meet. | |
RD-shade |
the background color (in hex format, e.g. #ABCDEF ) of all headers. Use RDm-shade or RDmh-shade for individual headers. |
#F2F2F2
|
RDm-RD(m+1)-path
[a] |
set to no or 0 to omit the paths from round m to round m+1. |
yes
|
paramstyle
[b] |
set to numbered change the parameter name style of RDm-textk , RDm-seedk , RDm-teamk , and RDm-scorek to a numbered notation (|1= ,|2= ...). Set |seeds=yes add seeds. |
indexed
|
|RD1-team3=
and |RD1b-team1=
can be used to assign the third team in the first column. By default, entries with subheader prefixes will override those without. In the below example, RD1b-team1
will override any value that has been set by RD1-team3
.Upper round | |||
Lower round | |||
RD1-team3 or RD1b-team1 | |||
|RD1-seed1=1
set. If ArticleX implemented {{NTeamBracket|RD1-seed1=2}}
, then the first team in round 1 would have a seed of 2.Path codes are entered in the form a-b
, where a
is associated match in the first column, and b
is associated match in the second column. Path codes can be grouped; for example, (a,b)-c
is equivalent to a-c, b-c
. To add color, append :color
to the end of a path, e.g. 3-5:red
. Only one extra color can be used in a bracket.
Example | Output | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1-3
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(1,5)-3
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
3-1,3-5:red
|
Note: These are only examples to illustrate parameters. Standard 4-team, 8-team, etc. brackets are better handled by Module:Team bracket.
{{#invoke:Build bracket|main | rounds=2 | col1-headers = 1 | col2-headers = 1 | col1-matches = 3,7 | col2-matches = 5 | col1-col2-paths = (3,7)-5 <!-- Defaults --> | RD2 = Grand Final | RD1-seed1 = 1 | RD1-seed3 = 2 }}
Semifinals | Grand Final | |||||||
1 | ||||||||
2 | ||||||||
{{#invoke:Build bracket|main | rounds=2 | teams-per-match = 3 | col1-headers = 1 | col2-headers = 1 | col1-matches = 3,7,11 | col2-matches = 7 | col1-col2-paths = (3,7,11)-7 }}
Semifinals | Final | |||||||
{{#invoke:Build bracket|main | rounds=4 | col1-headers = 1,7 | col2-headers = 1,7 | col3-headers = 7 | col4-headers = 1 | col1-matches = 4,11 | col2-matches = 3,10 | col3-matches = 9 | col4-matches = 6 | col1-col2-paths = 4-3, 11-10 | col2-col3-paths = 3-3, 10-9 | col3-col4-paths = (3,9)-6 <!-- Defaults --> | RD1 = Upper round 1 | RD2 = Upper final | RD3b = Lower final }}
Upper round 1 | Upper final | Final | ||||||||||||||||
Lower round 1 | Lower round 2 | Lower final | ||||||||||||||||
{{#invoke:Build bracket|main | rounds=2 | col1-matches = 3,7 | col2-matches = 3,7 | col1-col2-paths = 3-7, 7-3 | col1-col2-cross = 5 }}
Semifinals | Final | ||||||||
{{#invoke:Build bracket|main | rounds=2 | col1-matches = 3,6 | col2-matches = 4.5 | col1-col2-paths = (3,6)-4.5 | RD1-text1 = Text 1 | RD1-text2 = Text 2 | RD2-text1 = Text 3 }}
Semifinals | Final | |||||||
Text 1 | ||||||||
Text 3 | ||||||||
Text 2 | ||||||||
{{#invoke:Build bracket|main | rounds=3 | col1-matches = 3,6,9,12 | col2-matches = 4.5,10.5 | col3-matches = 7.5 | col1-col2-paths = (3,6)-4.5, (9,12)-10.5 | col2-col3-paths = (4.5,10.5)-7.5 | RD1-group1 = Group 1 | RD1-group2 = Group 2 | RD2-group1 = Group 3 }}
Quarterfinals | Semifinals | Final | |||||||||||
Group 1 | |||||||||||||
Group 3 | |||||||||||||
Group 2 | |||||||||||||
{{#invoke:Build bracket|main | rounds=3 | legs = 2 | RD1-legs = 3 | aggregate = y | col1-matches = 3,6,9,12 | col2-matches = 4.5,10.5 | col3-matches = 7.5 | col1-col2-paths = (3,6)-4.5, (9,12)-10.5 | col2-col3-paths = (4.5,10.5)-7.5 }}
Quarterfinals | Semifinals | Final | ||||||||||||||||||
{{#invoke:Build bracket|main | rounds=3 | RD1-byes = y | col1-matches = 3,6,9,12 | col2-matches = 4.5,10.5 | col3-matches = 7.5 | col1-col2-paths = (3,6)-4.5, (9,12)-10.5 | col2-col3-paths = (4.5,10.5)-7.5 | RD1-team1 = Team 1 | RD1-team2 = Team 2 }}
Quarterfinals | Semifinals | Final | |||||||||||
Team 1 | |||||||||||||
Team 2 | |||||||||||||
{{#invoke:Build bracket|main | rounds=2 | col1-matches = 3,7 | col2-matches = 5 | col1-col2-paths = (3,7)-5 | paramstyle = numbered | seeds = yes | Text 1 | 1 | Team 1 | 5 | 4 | Team 2 | 11 | Text 2 | 2 | Team 3 | 6 | 3 | Team 4 | 3 | Text 3 | 4 | Team 2 | 2 | 2 | Team 3 | 1 }}
Semifinals | Final | |||||||
Text 1 | ||||||||
1 | Team 1 | 5 | ||||||
4 | Team 2 | 11 | Text 3 | |||||
4 | Team 2 | 2 | ||||||
Text 2 | 2 | Team 3 | 1 | |||||
2 | Team 3 | 6 | ||||||
3 | Team 4 | 3 | ||||||
{{#invoke:Build bracket|main | rounds=2 | legs = 3 | boldwinner=y | col1-matches = 3,7 | col2-matches = 5 | col1-col2-paths = (3,7)-5 | RD1-seed1 = 1 | RD1-team1 = Team 1 | RD1-score1-1 = 5 | RD1-score1-2 = 12 | RD1-score1-3 = 15 | RD1-seed2 = 4 | RD1-team2 = Team 2 | RD1-score2-1 = 11 | RD1-score2-2 = 10 | RD1-score2-3 = 4 | RD1-seed3 = 2 | RD1-team3 = Team 3 | RD1-score3-1 = 6 | RD1-score3-2 = 13 | RD1-score3-3 = {{ndash}} | RD1-seed4 = 3 | RD1-team4 = Team 4 | RD1-score4-1 = 3 | RD1-score4-2 = 2 | RD1-score4-3 = {{ndash}} | RD2-seed1 = 4 | RD2-team1 = Team 2 | RD2-score1-1 = 2 | RD2-score1-2 = 2 | RD2-score1-3 = 5 | RD2-seed2 = 2 | RD2-team2 = Team 3 | RD2-score2-1 = 1 | RD2-score2-2 = 7 | RD2-score2-3 = 2 }}
Semifinals | Final | |||||||||||
1 | Team 1 | 5 | 12 | 15 | ||||||||
4 | Team 2 | 11 | 10 | 4 | ||||||||
4 | Team 2 | 2 | 2 | 5 | ||||||||
2 | Team 3 | 1 | 7 | 2 | ||||||||
2 | Team 3 | 6 | 13 | – | ||||||||
3 | Team 4 | 3 | 2 | – | ||||||||
local p = {}
local entries = {}
local pathCell = {}
local crossCell = {}
local skipPath = {}
local shift = {}
local hascross = {}
local teams_per_match = {}
local rlegs = {}
local maxlegs = {}
local autolegs
local byes = {}
local hide = {}
local matchgroup = {}
local nowrap
local autocol
local seeds
local forceseeds
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 pargss or fargss
end
local function toChar(num)
return string.char(string.byte("a")+num-1)
end
local function unboldParenthetical(text)
-- Replace wikilinks with unique placeholders
local counter = 0
local placeholders = {}
text = text:gsub('%[%[(.-)%]%]', function(link)
counter = counter + 1
local placeholder = '__WIKILINK__' .. counter .. '__'
placeholdersplaceholder = link
return placeholder
end)
-- Apply <span style="font-weight:normal"></span> to parenthetical and bracketed text
text = text:gsub('(%b())', '<span style="font-weight:normal">%1</span>')
:gsub('(%b[])', '<span style="font-weight:normal">%1</span>')
-- Restore the original wikilinks
for placeholder, link in pairs(placeholders) do
text = text:gsub(placeholder, '[[' .. link .. ']]')
end
return text
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
matchgroupj={}
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
matchgroupj][i=math.ceil(entriesj][i]['index'/teams_per_matchj])
entriesj][i]['group' = math.ceil(entriesj][i]['index'/teams_per_matchj])
end
end
end
end
local function teamLegs(j,i)
local legs = rlegsj
if notempty(entriesj][i]['legs']) then
legs = tonumber(entriesj][i]['legs'])
end
if autolegs then
local l=1
repeat l=l+1
until isempty(entriesj][i]['score'][l])
legs = l-1
end
return legs
end
local function boldWinner()
local function boldScore(j,i,l)
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
local myscore = entriesj][i]['score'][l]:gsub('%W','')
if myscore == "" or myscore:find("%D") then return 'normal'
else myscore=tonumber(myscore) end
local compscore = {}
for k,v in pairs(matchgroupj]) do
if matchgroupj][i==v and k~=i then
local theirscore = entriesj][k]['score'][l or ''
theirscore = theirscore:gsub('%W','')
if theirscore== "" or theirscore:find("%D") then return 'normal'
else table.insert(compscore,tonumber(theirscore)) end
end
end
for k,v in pairs(compscore) do
if myscore<=v then return 'normal' end
end
if l~='agg' then
entriesj][i]['wins' = entriesj][i]['wins'+1
else
entriesj][i]['aggwins' = 1
end
return 'bold'
end
end
local function boldTeam(j,i,agg)
local wins
local legs = teamLegs(j,i)
if agg~=true then
wins = 'wins'
if entriesj][i][wins>legs/2 then
return 'bold'
end
if autolegs then
for l=1,legs do
if notempty(entriesj][i]['score'][l]) and string.find(entriesj][i]['score'][l],"nbsp") then
return 'normal'
end
end
else
for l=1,legs do
if isempty(entriesj][i]['score'][l]) or string.find(entriesj][i]['score'][l],"nbsp") then
return 'normal'
end
end
end
else
wins = 'aggwins'
end
local compteam = {}
for k,v in pairs(matchgroupj]) do
if matchgroupj][i==v and k~=i then
table.insert(compteam,tonumber(entriesj][k][wins]))
end
end
for k,v in pairs(compteam) do
if entriesj][i][wins<=v then
return 'normal'
end
end
return 'bold'
end
for j=minc,c do
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
entriesj][i]['wins' = 0
entriesj][i]['aggwins' = 0
end
end
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
local legs = teamLegs(j,i)
for l=1,legs do
entriesj][i]['score']['weight'][l = boldScore(j,i,l)
end
if aggregate and legs>1 then
entriesj][i]['score']['weight']['agg' = boldScore(j,i,'agg')
end
end
end
for i=1,r do
if entriesj][i~= nil and entriesj][i]['ctype'=='team' then
local agg
local legs = teamLegs(j,i)
if aggregate and legs>1 then agg=true end
entriesj][i]['weight' = boldTeam(j,i,agg)
end
end
end
end
local function isBlankEntry(col,row,ctype)
if isempty(entriescol][row]) then return true end
if isempty(entriescol][row]['team']) and isempty(entriescol][row]['text']) then return true end
return false
end
local function showSeeds(j,i)
local showseed=false
if forceseeds or notempty(entriesj][i]['seed']) then
showseed=true
else
for k=1,teams_per_matchj-1 do
if notempty(entriesj][i+2*k]) and entriesj][i]['group'==entriesj][i+2*k]['group' and notempty(entriesj][i+2*k]['seed']) then
showseed=true
end
if notempty(entriesj][i-2*k]) and entriesj][i]['group'==entriesj][i-2*k]['group' and notempty(entriesj][i-2*k]['seed']) then
showseed=true
end
end
end
return showseed
end
local function cellBorder(b)
return b1..'px '..b2..'px '..b3..'px '..b4..'px'
end
local function Cell(tbl,j,i,rowspan,colspan,text,align,border,border_width,bg,padding,weight,nwrap)
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(border) then
cell:css('border',border)
end
if notempty(border_width) then
cell:css('border-width',cellBorder(border_width))
end
if notempty(bg) then
cell:css('background-color',bg)
end
if notempty(align) then
cell:css('text-align',align)
end
cell:css('padding','0em 0.3em')
if weight=='bold' then
cell:css('font-weight',weight)
end
if notempty(text) then
cell:wikitext(text)
end
return cell
end
local function teamCell(tbl,k,j,i,l,colspan)
local bg = '#F2F2F2'
local align
local padding
local weight
local text
local nwrap
local b={0,0,1,1}
if k=='seed' or k=='score' then
align='center'
end
if k~='seed' then
bg='#F9F9F9'
end
if k=='team' then
padding='0.3em'
if teamLegs(j,i)==0 then
b2=1
end
end
if entriesj][i]['position'=='top' then
b1=1
end
if l==teamLegs(j,i) or l=='agg' or k=='seed' then
b2=1
end
if (l==nil and entriesj][i]['weight'=='bold') or entriesj][i]['score']['weight'][l=='bold' then
weight='bold'
end
if l==nil then
text=unboldParenthetical(entriesj][i][k])
else
text=tostring(entriesj][i][k][l])
end
return Cell(tbl,j,i,2,colspan,text,align,'solid #aaa',b,bg,padding,weight,nwrap)
end
local function insertEntry(tbl,j,i)
local entry_colspan=maxlegsj+2
if not seeds then entry_colspan=entry_colspan-1 end
if (aggregate and maxlegsj>1) or maxlegsj==0 then
entry_colspan=entry_colspan+1
end
if entriesj][i~=nil and entriesj][i]['ctype'=='blank' then
return
end
if entriesj][i==nil then
if entriesj][i-1~=nil or i==1 then
local rowspan = 0
local row = i
repeat
rowspan=rowspan+1
row=row+1
until entriesj][row~=nil or row>r
return Cell(tbl,j,i,rowspan,entry_colspan)
else
return
end
end
if entriesj][i]['ctype'=='header' then
if byesj][entriesj][i]['headerindex']] then
local emptyround = true
local row = i+1
repeat
if not isBlankEntry(j,row) then
emptyround = false
end
row = row+1
until (entriesj][row~=nil and entriesj][row]['ctype'=='header') or row>r
if emptyround == true then
return Cell(tbl,j,i,2,entry_colspan)
end
end
if hidej][entriesj][i]['headerindex']] then
return Cell(tbl,j,i,2,entry_colspan)
end
if isempty(entriesj][i]['header']) then
if entriesj][i]['headerindex'==1 then
if j==c then entriesj][i]['header' = 'Final'
elseif j==c-1 then entriesj][i]['header' = 'Semifinals'
elseif j==c-2 then entriesj][i]['header' = 'Quarterfinals'
else entriesj][i]['header' = 'Round '..j
end
else
entriesj][i]['header' = 'Lower round '..j
end
end
return Cell(tbl,j,i,2,entry_colspan,entriesj][i]['header'],'center','1px solid #aaa',nil,entriesj][i]['shade'])
end
if entriesj][i]['ctype'=='team' then
if (byesj][entriesj][i]['headerindex']] and isBlankEntry(j,i)) or hidej][entriesj][i]['headerindex']] then
return Cell(tbl,j,i,2,entry_colspan)
end
local legs = teamLegs(j,i)
local team_colspan = maxlegsj-legs+1
if aggregate and legs==1 and maxlegsj>1 then
team_colspan=team_colspan+1
end
if maxlegsj==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 entriesj][i]['ctype'=='text' then
Cell(tbl,j,i,2,entry_colspan,entriesj][i]['text'],nil,nil,nil,nil,'0.3em')
end
if entriesj][i]['ctype'=='group' then
local colspan=0
for m=j,entriesj][i]['colspan'+j-1 do
colspan=colspan+maxlegsm+2
if not seeds then colspan=colspan-1 end
if (aggregate and maxlegsm>1) or maxlegsm==0 then
colspan=colspan+1
end
end
colspan = colspan+2*(entriesj][i]['colspan'-1)
return Cell(tbl,j,i,2,colspan,entriesj][i]['group'],'center')
end
if entriesj][i]['ctype'=='line' then
local b={0,0,0,0}
b3=2*pathCellj-1][i+1][3][1][3
return Cell(tbl,j,i,2,entry_colspan,entriesj][i]['text'],nil,'solid black',b)
end
if entriesj][i]['ctype'=='line2' then
local b={0,0,0,0}
b1=2*pathCellj-1][i][3][1][1
return Cell(tbl,j,i,2,entry_colspan,entriesj][i]['text'],nil,'solid black',b)
end
end
local function isRoundHidden(j,i,headerindex)
if notempty(entriesj][i]['pheader']) then
hidej][entriesj][i]['headerindex']] = false
end
local row = i+1
repeat
if not isBlankEntry(j,row) then
hidej][entriesj][i]['headerindex']] = false
end
row = row+1
until (entriesj][row~=nil and entriesj][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(entriesj][i]['headerindex']),bargs('RD'..j..toChar(entriesj][i]['headerindex'])..'-altname') or 'RD'..j..toChar(entriesj][i]['headerindex'])}
}
local name = {cname, bargs(cname..'-altname') or cname}
local index = {entriesj][i]['index'], entriesj][i]['altindex']}
local result = {}
if cname=='header' then
if entriesj][i]['headerindex'==1 then
for k=1,2 do
table.insert(result,bargs(rname1][3-k]) or '')
table.insert(result,bargs(rname2][3-k]) or '')
end
else
for k=1,2 do
table.insert(result,bargs(rname2][3-k]) or '')
end
end
elseif cname=='pheader' then
if entriesj][i]['headerindex'==1 then
for k=1,2 do
table.insert(result,pargsrname1][3-k]] or '')
table.insert(result,pargsrname2][3-k]] or '')
end
else
for k=1,2 do
table.insert(result,pargsrname2][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(rname3-m][3-k..'-'..name1..index3-m]) or bargs(rname3-m][3-k..'-'..name1..'0'..index3-m]) or '')
end
table.insert(result,bargs(rname3-m][3-k..'-'..name1..index3-m..'-'..l) or bargs(rname3-m][3-k..'-'..name1..'0'..index3-m..'-'..l) or '')
end end
elseif cname=='shade' then
for k=1,2 do
if entriesj][i]['headerindex'==1 then
table.insert(result,bargs(rname1][3-k..'-'..name1]) or '')
else
table.insert(result,bargs(rname2][3-k..'-'..name1]) 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(rname3-m][3-k..'-'..name3-n..index3-m]) or bargs(rname3-m][3-k..'-'..name3-n..'0'..index3-m]) or '')
end end end
else
for m=1,2 do for k=1,2 do
table.insert(result,bargs(rname3-m][3-k..'-'..name1..index3-m]) or bargs(rname3-m][3-k..'-'..name1..'0'..index3-m]) or '')
end end
end
for k=1,#result do
if notempty(resultk]) then
return resultk
end
end
return ''
end
local function indexedParams(j)
for i=1,r do
if entriesj][i~=nil then
if entriesj][i]['ctype'=='team' then
local legs = rlegsj
if forceseeds then
entriesj][i]['seed' = bargs(masterindex) or ''
masterindex = masterindex+1
end
entriesj][i]['team' = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
entriesj][i]['legs' = paramNames('legs',j,i)
entriesj][i]['score' = {}
entriesj][i]['weight' = 'normal'
entriesj][i]['score']['weight' = {}
if notempty(entriesj][i]['legs']) then
legs = tonumber(entriesj][i]['legs'])
end
for l=1,legs do
entriesj][i]['score'][l = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
entriesj][i]['score']['weight'][l = 'normal'
end
if aggregate and legs>1 then
entriesj][i]['score']['agg' = bargs(masterindex) or ''
masterindex = masterindex+1
entriesj][i]['score']['weight']['agg' = 'normal'
end
end
if entriesj][i]['ctype'=='header' then
entriesj][i]['header' = paramNames('header',j,i)
entriesj][i]['pheader' = paramNames('pheader',j,i)
entriesj][i]['shade' = paramNames('shade',j,i)
end
if entriesj][i]['ctype'=='text' then
entriesj][i]['text' = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
end
if entriesj][i]['ctype'=='group' then
entriesj][i]['group' = bargs(tostring(masterindex)) or ''
masterindex = masterindex+1
end
if entriesj][i]['ctype' == 'line' and entriesj][i]['hastext'==true then
entriesj][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
rlegsj = 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 entriesj][i~=nil then
if entriesj][i]['ctype'=='team' then
local legs = rlegsj
entriesj][i]['seed' = paramNames('seed',j,i)
entriesj][i]['team' = paramNames('team',j,i)
entriesj][i]['legs' = paramNames('legs',j,i)
entriesj][i]['score' = {}
entriesj][i]['weight' = 'normal'
entriesj][i]['score']['weight' = {}
if notempty(entriesj][i]['legs']) then
legs = tonumber(entriesj][i]['legs'])
end
if autolegs then
local l=1
repeat
entriesj][i]['score'][l = paramNames('score',j,i,l)
entriesj][i]['score']['weight'][l = 'normal'
l=l+1
until isempty(paramNames('score',j,i,l))
legs = l-1
else
for l=1,legs do
entriesj][i]['score'][l = paramNames('score',j,i,l)
entriesj][i]['score']['weight'][l = 'normal'
end
end
if aggregate and legs>1 then
entriesj][i]['score']['agg' = paramNames('score',j,i,'agg')
entriesj][i]['score']['weight']['agg' = 'normal'
end
end
if entriesj][i]['ctype'=='header' then
entriesj][i]['header' = paramNames('header',j,i)
entriesj][i]['pheader' = paramNames('pheader',j,i)
entriesj][i]['shade' = paramNames('shade',j,i)
end
if entriesj][i]['ctype'=='text' then
entriesj][i]['text' = paramNames('text',j,i)
end
if entriesj][i]['ctype'=='group' then
entriesj][i]['group' = paramNames('group',j,i)
end
if entriesj][i]['ctype' == 'line' and entriesj][i]['hastext'==true then
entriesj][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 entriesj][i~=nil and entriesj][i]['ctype'=='header' then
isRoundHidden(j,i)
end
if entriesj][i~=nil and not hidej][entriesj][i]['headerindex']] then
if not byesj][entriesj][i]['headerindex']] or (byesj][entriesj][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,headerindexj do
if byesj][k or hidej][k then
r=byerows+1
end
end
end
if autocol then
c = maxcol
end
end
local function getHide(j,headerindex)
hidej = {}
for k=1,headerindexj do
if bargs('RD'..j..toChar(k)..'-hide')=='yes' or bargs('RD'..j..toChar(k)..'-hide')=='y' then
hidej][k=true
end
end
end
local function getByes(j,headerindex)
byesj = {}
for k=1,headerindexj do
if bargs('byes')=='yes' or bargs('byes')=='y' then
byesj][k=true
elseif tonumber(bargs('byes')) then
if j<=tonumber(bargs('byes')) then
byesj][k=true
end
else
byesj][k=false
end
if bargs('RD'..j..'-byes')=='yes' or bargs('RD'..j..'-byes')=='y' then
byesj][k=true
elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then
byesj][k=false
end
if bargs('RD'..j..toChar(k)..'-byes')=='yes' or bargs('RD'..j..toChar(k)..'-byes')=='y' then
byesj][k=true
elseif bargs('RD'..j..'-byes')=='no' or bargs('RD'..j..'-byes')=='n' then
byesj][k=false
end
end
end
local function getAltIndices()
local teamindex=1
local textindex=1
local groupindex=1
for j=minc,c do
headerindexj=0
for i=1,r do
if entriesj][i==nil and i==1 then
headerindexj=headerindexj+1
end
if entriesj][i~=nil then
if entriesj][i]['ctype' == 'header' then
entriesj][i]['altindex' = headerindexj
teamindex=1
textindex=1
headerindexj=headerindexj+1
elseif entriesj][i]['ctype' == 'team' then
entriesj][i]['altindex' = teamindex
teamindex=teamindex+1
elseif entriesj][i]['ctype' == 'text' then
entriesj][i]['altindex' = textindex
textindex=textindex+1
elseif entriesj][i]['ctype' == 'group' then
entriesj][i]['altindex' = groupindex
groupindex=groupindex+1
elseif entriesj][i]['ctype' == 'line' and entriesj][i]['hastext'==true then
entriesj][i]['altindex' = textindex
textindex=textindex+1
end
entriesj][i]['headerindex' = headerindexj
end
end
getByes(j,headerindex)
getHide(j,headerindex)
end
end
local function noPaths(j,i)
local result = true
local cols = 2
if hascrossj==true then
cols = 3
end
for k=1,cols do
for n=1,4 do
if pathCellj][i][k][1][n~=0 then
result = false
return result
end
end
end
if hascrossj==true and (crossCellj][i]['left'][1==1 or crossCellj][i]['right'][1==1) then
result = false
return result
end
return result
end
local function generatePathCell(tbl,j,i,k,bg,rowspan)
local color = pathCellj][i][k]['color'
if not hascrossj and k==2 then
return
end
local cell=tbl:tag('td')
local a=pathCellj][i
if rowspan~=1 then
cell:attr('rowspan',rowspan)
end
if notempty(bg) and k==2 then
cell:css('background',bg)
:css('transform','translate(-1px)')
end
if ak][1][1~=0 or ak][1][2~=0 or ak][1][3~=0 or ak][1][4~=0 then
cell:css('border','solid '..color)
:css('border-width',2*ak][1][1..'px '..2*ak][1][2..'px '..2*ak][1][3..'px '..2*ak][1][4..'px')
end
return cell
end
local function insertPath(tbl,j,i)
if skipPathj][i then
return
end
local colspan = 2
local rowspan = 1
local angle = 58.2
local pathcolor
local bg = ''
local cross = {'',''}
if i<r then
local function repeatedPath(a)
if a>r-1 or skipPathj][a then
return false
end
for k=1,3 do
for n=1,4 do
if pathCellj][i][k][1][n~=pathCellj][a][k][1][n then
return false
end
end
end
return true
end
if repeatedPath(i) then
local row=i
repeat
if row~=i and repeatedPath(row) then
skipPathj][row=true
end
rowspan=rowspan+1
row=row+1
until row>r or not repeatedPath(row)
rowspan=rowspan-1
end
end
if i>1 and (crossCellj][i-1]['left'][1==1 or crossCellj][i-1]['right'][1==1) then
return
end
if hascrossj then
colspan = 3
if crossCellj][i]['left'][1==1 or crossCellj][i]['right'][1==1 then
rowspan = 2
if crossCellj][i]['left'][1==1 then
cross1 = 'linear-gradient(to top right, transparent calc(50% - 1px),'..crossCellj][i]['left'][2..' calc(50% - 1px),'..crossCellj][i]['left'][2..' calc(50% + 1px), transparent calc(50% + 1px))'
end
if crossCellj][i]['right'][1==1 then
cross2 = 'linear-gradient(to bottom right, transparent calc(50% - 1px),'..crossCellj][i]['right'][2..' calc(50% - 1px),'..crossCellj][i]['right'][2..' calc(50% + 1px), transparent calc(50% + 1px))'
end
end
if notempty(cross1]) and notempty(cross2]) then
cross1 = cross1..','
end
bg = cross1..cross2
end
for k=1,3 do
generatePathCell(tbl,j,i,k,bg,rowspan)
end
end
local function parsePaths(j)
local result={}
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 array = split(val:gsub("%s+",""):gsub("%)",""):gsub("%(",""),{"-"})
for k,_ in pairs(array) do
arrayk = split(arrayk],{","})
end
if notempty(array2]) then
for m=1,#array2 do
array3 = {}
array2][m = split(array2][m],{":"})
array3][m = array2][m][2
array2][m = array2][m][1
end
for n=1,#array1 do
for m=1,#array2 do
table.insert(result,{tonumber(array1][n]),tonumber(array2][m]),['color'=array3][m]})
end
end
end
end
return result
end
local function isPathHidden(j,i,start,stop)
local result=false
if notempty(entriesj][start-1]) and (byesj][entriesj][start-1]['headerindex']] and isBlankEntry(j,start-1) and isBlankEntry(j,start+1) or hidej][entriesj][start-1]['headerindex']]) then
if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then
result=true
end
end
if notempty(entriesj+1][stop-1]) and (byesj+1][entriesj+1][stop-1]['headerindex']] and isBlankEntry(j+1,stop-1) and isBlankEntry(j+1,stop+1) or hidej+1][entriesj+1][stop-1]['headerindex']])then
if bargs('show-bye-paths')~='y' and bargs('show-bye-paths')~='yes' then
result=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(entriesj][start-1]) and entriesj][start-1]['headerindex'==1 then
result=true
end
end
return result
end
local function getPaths()
local paths = {}
for j=minc,c-1 do
hascrossj = false
if notempty(fargs'col'..j..'-col'..(j+1)..'-cross']) then
hascrossj = true
end
end
for j=minc,c-1 do
local straightpaths = {}
local outpaths = {}
local inpaths = {}
pathsj=parsePaths(j)
pathCellj = {}
crossCellj = {}
skipPathj = {}
for i=1,r do
pathCellj][i = {}
crossCellj][i = {['left'={0,'black'},['right'={0,'black'}}
for k=1,3 do
pathCellj][i][k = {{0,0,0,0},['color'='black'}
end
skipPathj][i = false
end
local crossloc = split((fargs'col'..j..'-col'..(j+1)..'-cross' or ''):gsub("%s+", ""),{","},true)
if shiftj~=0 and notempty(crossloc1]) then
for n=1,#crossloc do
crosslocn = crosslocn+shiftj
end
end
for k,v in ipairs(pathsj]) do
local start = 2*(pathsj][k][1+shiftj])+(teams_per_matchj-2)
local stop = 2*(pathsj][k][2+shiftj+1])+(teams_per_matchj+1-2)
local mid = {}
local cross = 0
if notempty(crossloc1]) then
for n=1,#crossloc do
midn = 2*crosslocn+(teams_per_matchj-2)
end
else
mid1=0
end
for n=1,#mid do
if (start<stop and midn<stop and midn>start) or (start>stop and midn>stop and midn<start) then
cross = midn
end
end
pathsj][k]['color' = pathsj][k]['color' or 'black'
table.insert(outpaths,{start,pathsj][k]['color']})
table.insert(inpaths,{stop,pathsj][k]['color']})
if not isPathHidden(j,i,start,stop) then
if start==stop then
table.insert(straightpaths,{start,pathsj][k]['color']})
elseif start<stop then
if stop>r then break end
pathCellj][start+1][1][1][1 = 1
pathCellj][start+1][1]['color' = pathsj][k]['color'
if cross==0 then
if hascrossj then
pathCellj][start+1][2][1][1 = 1
pathCellj][start+1][2]['color' = pathsj][k]['color'
for i=start+1,stop do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
else
for i=start+1,stop do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
end
else
crossCellj][cross]['left' = {1,pathsj][k]['color']}
for i=start+1,cross-1 do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
for i=cross+2,stop do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
end
pathCellj][stop][3][1][3 = 1
pathCellj][stop][3]['color' = pathsj][k]['color'
elseif start>stop then
if start>r then break end
pathCellj][stop+1][3][1][1 = 1
pathCellj][stop+1][3]['color' = pathsj][k]['color'
if cross==0 then
if hascrossj then
for i=stop+1,start do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
pathCellj][start][2][1][3 = 1
pathCellj][start][2]['color' = pathsj][k]['color'
else
for i=stop+1,start do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
end
else
crossCellj][cross]['right' = {1,pathsj][k]['color']}
for i=stop+1,cross-1 do
pathCellj][i][2][1][2 = 1
pathCellj][i][2]['color' = pathsj][k]['color'
end
for i=cross+2,start do
pathCellj][i][1][1][2 = 1
pathCellj][i][1]['color' = pathsj][k]['color'
end
end
pathCellj][start][1][1][3 = 1
pathCellj][start][1]['color' = pathsj][k]['color'
end
end
end
-- Thicken start==stop paths
for n=1,#straightpaths do
local i = straightpathsn][1
local color = straightpathsn][2
if i>r then break end
if pathCellj][i][1][1][3==0 then
pathCellj][i][1][1][3 = 1
pathCellj][i][2][1][3 = 1
pathCellj][i][3][1][3 = 1
pathCellj][i][1]['color' = color
pathCellj][i][2]['color' = color
pathCellj][i][3]['color' = color
if pathCellj][i+1][1][1][1==0 then
pathCellj][i+1][1][1][1 = 0.5
pathCellj][i+1][2][1][1 = 0.5
pathCellj][i+1][3][1][1 = 0.5
pathCellj][i+1][1]['color' = color
pathCellj][i+1][2]['color' = color
pathCellj][i+1][3]['color' = color
end
elseif pathCellj][i+1][1][1][1==0 then
pathCellj][i+1][1][1][1 = 1
pathCellj][i+1][1]['color' = color
if hascrossj then
pathCellj][i+1][2][1][1 = 1
pathCellj][i+1][2]['color' = color
end
pathCellj][i+1][3][1][1 = 1
pathCellj][i+1][3]['color' = color
end
end
-- Thicken/Thin out paths
for n=1,#outpaths do
local i = outpathsn][1
local color = outpathsn][2
if i<r and pathCellj][i][1][1][3==1 and pathCellj][i+1][1][1][1==0 then
pathCellj][i+1][1][1][1 = 0.5*pathCellj][i][1][1][3
pathCellj][i+1][2][1][1 = 0.5*pathCellj][i][2][1][3
pathCellj][i+1][1]['color' = pathCellj][i][1]['color'
pathCellj][i+1][2]['color' = pathCellj][i][2]['color'
elseif i<r and pathCellj][i][1][1][3==0 and pathCellj][i+1][1][1][1==1 then
pathCellj][i][1][1][3 = pathCellj][i+1][1][1][1
pathCellj][i][2][1][3 = pathCellj][i+1][2][1][1
pathCellj][i+1][1][1][1 = 0.5*pathCellj][i][1][1][3
pathCellj][i+1][2][1][1 = 0.5*pathCellj][i][2][1][3
pathCellj][i][1]['color' = pathCellj][i+1][1]['color'
pathCellj][i][2]['color' = pathCellj][i+1][2]['color'
end
end
-- Thin double-in paths
for n=1,#inpaths do
local i = inpathsn][1
local color = inpathsn][2
if i<r and pathCellj][i][3][1][3==1 and pathCellj][i+1][3][1][1==1 and pathCellj][i][3]['color'==pathCellj][i+1][3]['color' then
pathCellj][i+1][3][1][1 = 0.5*pathCellj][i][3][1][3
end
end
end
for j=minc,c-1 do
for i=1,r-1 do
local straightpath=false
if (entriesj+1][i-1==nil or (byesj+1][entriesj+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) then
if (pathCellj][i][3][1][3~=0 and pathCellj+1][i][1][1][3~=0) or (pathCellj][i+1][3][1][1~=0 and pathCellj+1][i+1][1][1][1~=0) then
if pathCellj+1][i][1][1][3==pathCellj+1][i][3][1][3 and pathCellj+1][i+1][1][1][1==pathCellj+1][i+1][3][1][1 then
straightpath=true
end
pathCellj+1][i][1][1][3=pathCellj][i][3][1][3
pathCellj+1][i+1][1][1][1=pathCellj][i+1][3][1][1
pathCellj+1][i][2][1][3=pathCellj][i][3][1][3
pathCellj+1][i+1][2][1][1=pathCellj][i+1][3][1][1
entriesj+1][i-1={['ctype'='line'}
entriesj+1][i={['ctype'='blank'}
if notempty(entriesj+1][i+1]) then
entriesj+1][i+1]['ctype' = 'line2'
else
entriesj+1][i+1={['ctype'='line2'}
end
entriesj+1][i+2={['ctype'='blank'}
if straightpath then
pathCellj+1][i][3][1][3=pathCellj+1][i][1][1][3
pathCellj+1][i+1][3][1][1=pathCellj+1][i+1][1][1][1
end
end
end
end
end
end
local function getGroups()
local function check(j,i)
local result=false
if entriesj][i == nil then
if entriesj][i+1 == nil then
result=true
elseif entriesj][i+1]['ctype'=='text' and isBlankEntry(j,i+1) then
result=true
end
elseif entriesj][i]['ctype'=='text' and isBlankEntry(j,i) then
result=true
end
return result
end
for j=minc,c-1 do
if teams_per_matchj==2 then
local n=0
for i=1,r-1 do
if pathCellj][i][3][1][3==1 or pathCellj][i+1][3][1][1==1 then
n=n+1
if check(j,i) then
local k=minc-1
repeat
if entriesj-k][i+1~=nil and entriesj-k][i+1]['ctype'=='text' and isBlankEntry(j-k,i+1) then
entriesj-k][i+2=nil
end
entriesj-k][i={['ctype'='blank'}
entriesj-k][i+1={['ctype'='blank'}
if k>0 and noPaths(j-k,i) then
skipPathj-k][i = true
skipPathj-k][i+1 = true
end
k=k+1
until k>j-1 or not check(j-k,i) or not noPaths(j-k,i)
k=k-1
entriesj-k][i={['ctype'='group',['index'=n,['colspan'=k+1}
entriesj-k][i+1={['ctype'='blank'}
entriesj-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_matchj = 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_matchj])
end
for j=minc,c do
entriesj = {}
shiftj = tonumber(bargs('RD'..j..'-shift')) or tonumber(bargs('shift')) or 0
colentryj = {
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(colentryj][1],1)
end
end
for j=minc,c do
local textindex=0
for k,v in ipairs(colentryj]) do
table.sort(colentryj][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,#colentryj][k do
if shiftj~=0 and colentryj][k][n>1 then
colentryj][k][n = colentryj][k][n+shiftj
end
local i=2*colentryj][k][n-1
maxrow = math.max(i+2*teams_per_matchj-1,maxrow)
if ctype=='team' then
if entriesj][i-1==nil and entriesj][i-2==nil then
entriesj][i-2={['ctype'='text',['index'=n}
entriesj][i-1={['ctype'='blank'}
textindex=n
end
entriesj][i={['ctype'=ctype,['index'=teams_per_matchj*n-(teams_per_matchj-1),['position'='top'}
entriesj][i+1={['ctype'='blank'}
for m=2,teams_per_matchj do
entriesj][i+2*(m-1)]={['ctype'=ctype,['index'=teams_per_matchj*n-(teams_per_matchj-m)}
entriesj][i+2*(m-1)+1={['ctype'='blank'}
end
elseif ctype=='text' then
entriesj][i={['ctype'=ctype,['index'=textindex+n}
entriesj][i+1={['ctype'='blank'}
elseif ctype=='line' then
entriesj][i={['ctype'=ctype}
entriesj][i+1={['ctype'='blank'}
entriesj][i+2={['ctype'='line2'}
entriesj][i+3={['ctype'='blank'}
elseif ctype=='group' then
entriesj][i={['ctype'=ctype,['index'=n}
entriesj][i+1={['ctype'='blank'}
else
entriesj][i={['ctype'=ctype,['index'=n,['position'='top'}
entriesj][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
local height = bargs('height') or 0
maxtpm = 1
seeds = true
nowrap = 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('paramstyle')=='numbered' then
paramstyle = 'numbered'
else
paramstyle = 'indexed'
end
if pargs.nowrap=='n' or pargs.nowrap=='no' then nowrap=false end
getCells()
getAltIndices()
assignParams()
matchGroups()
if (boldwinner=='yes' or boldwinner=='y' or boldwinner=='high') then boldWinner() end
getPaths()
if minc==1 then
getGroups()
end
for j=minc,c do
maxlegsj = rlegsj
for i=1,r do
if notempty(entriesj][i]) then
if notempty(entriesj][i]['legs']) then
maxlegsj = math.max(rlegsj],entriesj][i]['legs'])
end
if autolegs then
local l=1
repeat l=l+1
until isempty(entriesj][i]['score']) or isempty(entriesj][i]['score'][l])
maxlegsj = math.max(maxlegsj],l-1)
end
end
end
end
local div = mw.html.create('div')
:css('overflow','auto')
if height~=0 then
div:css('height',height)
end
local tbl = mw.html.create('table')
:attr('cellpadding','0')
:attr('cellspacing','0')
:css('font-size','90%')
:css('border-collapse','separate')
:css('margin','1em 2em 0em 1em')
if nowrap then
tbl:css('white-space','nowrap')
end
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 maxlegsj==0 then
tbl:tag('td'):css('width',getWidth('score','25px'))
else
for l=1,maxlegsj do
tbl:tag('td'):css('width',getWidth('score','25px'))
end
end
if aggregate and maxlegsj>1 then
tbl:tag('td'):css('width',getWidth('agg',getWidth('score','25px')))
end
if j~=c then
if hascrossj then
tbl:tag('td'):css('width',colspacing+1-4 ..'px')
:css('padding-left','4px')
tbl:tag('td'):css('padding-left','5px')
:css('width','5px')
tbl:tag('td'):css('width',colspacing-1-2 ..'px')
:css('padding-right','2px')
else
tbl:tag('td'):css('width',colspacing+1-4 ..'px')
:css('padding-left','4px')
tbl:tag('td'):css('width',colspacing-1-2 ..'px')
:css('padding-right','2px')
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
div:wikitext(tostring(tbl))
return tostring(div)
end
return p