Details: See additional infos and release notes below !
About
This module is intended to be used to create and edit intricate sports brackets that cannot be made by Module:TeamBracket or Module:RoundN. 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.
Additional infos
- This module is originally from Wikipedia but was altered to fit the needs of the Puroresu System Wiki.
→ Do not update it to a newer WP-version without going through the change log below. - Note that whenever possible, module TeamBracket and its templates should be used. Only use this module if it's absolutely necessary.
- The module uses the same color variables as module TeamBracket, adapting for light & dark mode.
The variables are defined in MediaWiki:TournamentBrackets.css - This version has additional parameters that the original WP module does not have:
class
andstyle
. They can be used to add css class(es) and/or inline css to the table.- Note that the table already gets the default class
tbr
(like the tables produced by module TeamBracket), and additionallybbr
(for build bracket) to address only tables made with this module.
- Note that the table already gets the default class
- The module also categorized pages it is used on in a tracking category Pages using module Build bracket. This can be suppressed with parameter
nocat
if needed. - See also Project: Tournament brackets help for general information about the usage of the team brackets on this wiki.
Usage
{{#invoke:Build bracket|main | rounds = | col1-headers = | col1-matches = | col2-headers = | col2-matches = ... | col1-col2-paths = | col2-col3-paths = ... }}
Parameters
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). |
8
|
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) |
160
|
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) |
40
|
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) |
40
|
seeds |
Set to no to omit seeds 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 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. |
css-variable--tbr-bracket-bg-color depends on color mode |
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
|
| ||
class |
This parameter can be used to add one or several css classes to the table produced by the module. Note that all tables already have the class tbr and bbr added by default. |
|
style |
This parameter can be used to add custom inline styles to the table. For example, a table could be centered on the page with this: |style = margin:0 auto; |
|
nocat |
This parameter can be used to suppress the categorization of pages the module is used on. By default, pages using this module are categorized into Pages using module Build bracket. Define with any value to activate. |
Notes
- a Does not currently work for paths under subheaders.
- b
numbered
may not be compatible with certain other features, e.g.boldwinner
and groups.
Parameter hierarchy
- Whenever there are multiple headers in a single column, more than one parameter may be assigned to a cell value. For example, in the following bracket, both
RD1-team3
andRD1b-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 byRD1-team3
.
Upper round | |||
Lower round | |||
RD1-team3 or RD1b-team1 | |||
- Parameters used in articles take precedence over parameters used in the template itself. For example, suppose ArticleX used the template NTeamBracket, and suppose NTeamBracket had the parameter
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
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
|
Examples
Note: These are only examples to illustrate parameters. Standard 4-team, 8-team, etc. brackets are better handled by Module:TeamBracket.
Standard 4-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 | ||||||||
3-way bracket
{{#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 | |||||||
Double-elimination bracket
{{#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 | ||||||||||||||||
Crossing paths
{{#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 | ||||||||
Text
{{#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 | ||||||||
Groups
{{#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 | |||||||||||||
Legs and aggregate
{{#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 | ||||||||||||||||||
Byes
{{#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 | |||||||||||||
Numbered parameters
{{#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 | ||||||
Bold winner
{{#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 = - | RD1-seed4 = 3 | RD1-team4 = Team 4 | RD1-score4-1 = 3 | RD1-score4-2 = 2 | RD1-score4-3 = - | 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 | - | ||||||||
Release notes
Source
- Module:Build bracket on Wikipedia
↳ Revision: 2024-08-29
↳Adapted for Puroresu System Wiki by Oot42
Change log
- 2025-01-03 - Copied from Wikipedia and edited for use on Puroresu System Wiki by Oot42
- Changed colors to css variables to adapt for light/dark mode
- Added a css class
tbr
to the table produced - Updated this documentation
- 2025-01-04 - Applied to Puroresu System Wiki
- Changed default widths to match with module TeamBracket
- Added parameters
class
&style
to add optional additional classes & styles to the table
- 2025-01-05
- Added additional module specific class
bbr
- Added categorization of pages using the module
- Fixed background color issues (hopefully)
- Added additional module specific class
Notes
- All lines with changes are marked with a comment in the module's source.
⟶ To find all changes, search for "Oot42".
See also
- Module: TeamBracket
- Project: Tournament brackets help
- Category: Tournament bracket templates
- Category: Tournament bracket modules
- Module: Build bracket on Wikipedia
Module code
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 pargs[s] or fargs[s]
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 .. '__'
placeholders[placeholder] = 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
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 teamLegs(j,i)
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
return legs
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]:gsub('%W','')
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
local theirscore = entries[j][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
entries[j][i]['wins'] = entries[j][i]['wins']+1
else
entries[j][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 entries[j][i][wins]>legs/2 then
return 'bold'
end
if autolegs then
for l=1,legs do
if notempty(entries[j][i]['score'][l]) and string.find(entries[j][i]['score'][l],"nbsp") then
return 'normal'
end
end
else
for l=1,legs do
if isempty(entries[j][i]['score'][l]) or string.find(entries[j][i]['score'][l],"nbsp") then
return 'normal'
end
end
end
else
wins = 'aggwins'
end
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 entries[j][i][wins]<=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 = teamLegs(j,i)
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 = teamLegs(j,i)
if aggregate and legs>1 then agg=true end
entries[j][i]['weight'] = boldTeam(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 cellBorder(b)
return b[1]..'px '..b[2]..'px '..b[3]..'px '..b[4]..'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 = 'var(--tbr-bbg)' -- colorfix / Oot42 / --tbr-bracket-bg-color / was '#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 <-- original -- colorfix / Oot42 / 2025-01-05
-- bg='#F9F9F9' -- replaced by the following lines, not sure why,
-- end -- k~='seed' makes all cells beside seed in that color
if k=='seed' then -- this makes only the seed cells to use the color-var (transparent)
bg='var(--tbr-sbg)' -- colorfix / Oot42 / --tbr-seed-bg-color
end
if k=='team' then
padding='0.3em'
if teamLegs(j,i)==0 then
b[2]=1
end
end
if entries[j][i]['position']=='top' then
b[1]=1
end
if l==teamLegs(j,i) or l=='agg' or k=='seed' then
b[2]=1
end
if (l==nil and entries[j][i]['weight']=='bold') or entries[j][i]['score']['weight'][l]=='bold' then
weight='bold'
end
if l==nil then
text=unboldParenthetical(entries[j][i][k])
else
text=tostring(entries[j][i][k][l])
end
return Cell(tbl,j,i,2,colspan,text,align,'solid var(--tbr-bc)',b,bg,padding,weight,nwrap)
-- ^^ colorfix / Oot42 / --tbr-border-color / was 'solid #aaa'
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 Cell(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 Cell(tbl,j,i,2,entry_colspan)
end
end
if hide[j][entries[j][i]['headerindex']] then
return Cell(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 Cell(tbl,j,i,2,entry_colspan,entries[j][i]['header'],'center','1px solid var(--tbr-bc)',nil,entries[j][i]['shade'])
-- ^^ colorfix / Oot42 / --tbr-border-color / was '1px solid #aaa'
end
if entries[j][i]['ctype']=='team' then
if (byes[j][entries[j][i]['headerindex']] and isBlankEntry(j,i)) or hide[j][entries[j][i]['headerindex']] then
return Cell(tbl,j,i,2,entry_colspan)
end
local legs = teamLegs(j,i)
local 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
Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,nil,nil,nil,'0.3em')
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)
return Cell(tbl,j,i,2,colspan,entries[j][i]['group'],'center')
end
if entries[j][i]['ctype']=='line' then
local b={0,0,0,0}
b[3]=2*pathCell[j-1][i+1][3][1][3]
return Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,'solid var(--tbr-cnr)',b) -- colorfix / Oot42 / --tbr-connector-color / was 'solid black'
end
if entries[j][i]['ctype']=='line2' then
local b={0,0,0,0}
b[1]=2*pathCell[j-1][i][3][1][1]
return Cell(tbl,j,i,2,entry_colspan,entries[j][i]['text'],nil,'solid var(--tbr-cnr)',b) -- colorfix / Oot42 / --tbr-connector-color / was 'solid black'
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,'var(--tbr-hbg)') -- colorfix / Oot42 / --tbr-header-bg-color / was '#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
elseif tonumber(bargs('byes')) then
if j<=tonumber(bargs('byes')) then
byes[j][k]=true
end
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 noPaths(j,i)
local result = true
local cols = 2
if hascross[j]==true then
cols = 3
end
for k=1,cols do
for n=1,4 do
if pathCell[j][i][k][1][n]~=0 then
result = false
return result
end
end
end
if hascross[j]==true and (crossCell[j][i]['left'][1]==1 or crossCell[j][i]['right'][1]==1) then
result = false
return result
end
return result
end
local function generatePathCell(tbl,j,i,k,bg,rowspan)
local color = pathCell[j][i][k]['color']
if not hascross[j] and k==2 then
return
end
local cell=tbl:tag('td')
local a=pathCell[j][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 a[k][1][1]~=0 or a[k][1][2]~=0 or a[k][1][3]~=0 or a[k][1][4]~=0 then
cell:css('border','solid '..color)
:css('border-width',2*a[k][1][1]..'px '..2*a[k][1][2]..'px '..2*a[k][1][3]..'px '..2*a[k][1][4]..'px')
end
return cell
end
local function insertPath(tbl,j,i)
if skipPath[j][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 skipPath[j][a] then
return false
end
for k=1,3 do
for n=1,4 do
if pathCell[j][i][k][1][n]~=pathCell[j][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
skipPath[j][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 (crossCell[j][i-1]['left'][1]==1 or crossCell[j][i-1]['right'][1]==1) then
return
end
if hascross[j] then
colspan = 3
if crossCell[j][i]['left'][1]==1 or crossCell[j][i]['right'][1]==1 then
rowspan = 2
if crossCell[j][i]['left'][1]==1 then
cross[1] = 'linear-gradient(to top right, transparent calc(50% - 1px),'..crossCell[j][i]['left'][2]..' calc(50% - 1px),'..crossCell[j][i]['left'][2]..' calc(50% + 1px), transparent calc(50% + 1px))'
end
if crossCell[j][i]['right'][1]==1 then
cross[2] = 'linear-gradient(to bottom right, transparent calc(50% - 1px),'..crossCell[j][i]['right'][2]..' calc(50% - 1px),'..crossCell[j][i]['right'][2]..' calc(50% + 1px), transparent calc(50% + 1px))'
end
end
if notempty(cross[1]) and notempty(cross[2]) then
cross[1] = cross[1]..','
end
bg = cross[1]..cross[2]
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
array[k] = split(array[k],{","})
end
if notempty(array[2]) then
for m=1,#array[2] do
array[3] = {}
array[2][m] = split(array[2][m],{":"})
array[3][m] = array[2][m][2]
array[2][m] = array[2][m][1]
end
for n=1,#array[1] do
for m=1,#array[2] do
table.insert(result,{tonumber(array[1][n]),tonumber(array[2][m]),['color']=array[3][m]})
end
end
end
end
return result
end
local function isPathHidden(j,i,start,stop)
local result=false
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
result=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
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(entries[j][start-1]) and entries[j][start-1]['headerindex']==1 then
result=true
end
end
return result
end
local function getPaths()
local paths = {}
for j=minc,c-1 do
hascross[j] = false
if notempty(fargs['col'..j..'-col'..(j+1)..'-cross']) then
hascross[j] = true
end
end
for j=minc,c-1 do
local straightpaths = {}
local outpaths = {}
local inpaths = {}
paths[j]=parsePaths(j)
pathCell[j] = {}
crossCell[j] = {}
skipPath[j] = {}
for i=1,r do
pathCell[j][i] = {}
crossCell[j][i] = {['left']={0,'var(--tbr-cnr)'},['right']={0,'var(--tbr-cnr)'}} -- colorfix / Oot42 / --tbr-connector-color / was 'black'
for k=1,3 do
pathCell[j][i][k] = {{0,0,0,0},['color']='var(--tbr-cnr)'} -- colorfix / Oot42 / --tbr-connector-color / was 'black'
end
skipPath[j][i] = false
end
local crossloc = split((fargs['col'..j..'-col'..(j+1)..'-cross'] or ''):gsub("%s+", ""),{","},true)
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 start = 2*(paths[j][k][1]+shift[j])+(teams_per_match[j]-2)
local stop = 2*(paths[j][k][2]+shift[j+1])+(teams_per_match[j+1]-2)
local mid = {}
local cross = 0
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
for n=1,#mid do
if (start<stop and mid[n]<stop and mid[n]>start) or (start>stop and mid[n]>stop and mid[n]<start) then
cross = mid[n]
end
end
paths[j][k]['color'] = paths[j][k]['color'] or 'var(--tbr-cnr)' -- colorfix / Oot42 / --tbr-connector-color / was 'black'
table.insert(outpaths,{start,paths[j][k]['color']})
table.insert(inpaths,{stop,paths[j][k]['color']})
if not isPathHidden(j,i,start,stop) then
if start==stop then
table.insert(straightpaths,{start,paths[j][k]['color']})
elseif start<stop then
if stop>r then break end
pathCell[j][start+1][1][1][1] = 1
pathCell[j][start+1][1]['color'] = paths[j][k]['color']
if cross==0 then
if hascross[j] then
pathCell[j][start+1][2][1][1] = 1
pathCell[j][start+1][2]['color'] = paths[j][k]['color']
for i=start+1,stop do
pathCell[j][i][2][1][2] = 1
pathCell[j][i][2]['color'] = paths[j][k]['color']
end
else
for i=start+1,stop do
pathCell[j][i][1][1][2] = 1
pathCell[j][i][1]['color'] = paths[j][k]['color']
end
end
else
crossCell[j][cross]['left'] = {1,paths[j][k]['color']}
for i=start+1,cross-1 do
pathCell[j][i][1][1][2] = 1
pathCell[j][i][1]['color'] = paths[j][k]['color']
end
for i=cross+2,stop do
pathCell[j][i][2][1][2] = 1
pathCell[j][i][2]['color'] = paths[j][k]['color']
end
end
pathCell[j][stop][3][1][3] = 1
pathCell[j][stop][3]['color'] = paths[j][k]['color']
elseif start>stop then
if start>r then break end
pathCell[j][stop+1][3][1][1] = 1
pathCell[j][stop+1][3]['color'] = paths[j][k]['color']
if cross==0 then
if hascross[j] then
for i=stop+1,start do
pathCell[j][i][2][1][2] = 1
pathCell[j][i][2]['color'] = paths[j][k]['color']
end
pathCell[j][start][2][1][3] = 1
pathCell[j][start][2]['color'] = paths[j][k]['color']
else
for i=stop+1,start do
pathCell[j][i][1][1][2] = 1
pathCell[j][i][1]['color'] = paths[j][k]['color']
end
end
else
crossCell[j][cross]['right'] = {1,paths[j][k]['color']}
for i=stop+1,cross-1 do
pathCell[j][i][2][1][2] = 1
pathCell[j][i][2]['color'] = paths[j][k]['color']
end
for i=cross+2,start do
pathCell[j][i][1][1][2] = 1
pathCell[j][i][1]['color'] = paths[j][k]['color']
end
end
pathCell[j][start][1][1][3] = 1
pathCell[j][start][1]['color'] = paths[j][k]['color']
end
end
end
-- Thicken start==stop paths
for n=1,#straightpaths do
local i = straightpaths[n][1]
local color = straightpaths[n][2]
if i>r then break end
if pathCell[j][i][1][1][3]==0 then
pathCell[j][i][1][1][3] = 1
pathCell[j][i][2][1][3] = 1
pathCell[j][i][3][1][3] = 1
pathCell[j][i][1]['color'] = color
pathCell[j][i][2]['color'] = color
pathCell[j][i][3]['color'] = color
if pathCell[j][i+1][1][1][1]==0 then
pathCell[j][i+1][1][1][1] = 0.5
pathCell[j][i+1][2][1][1] = 0.5
pathCell[j][i+1][3][1][1] = 0.5
pathCell[j][i+1][1]['color'] = color
pathCell[j][i+1][2]['color'] = color
pathCell[j][i+1][3]['color'] = color
end
elseif pathCell[j][i+1][1][1][1]==0 then
pathCell[j][i+1][1][1][1] = 1
pathCell[j][i+1][1]['color'] = color
if hascross[j] then
pathCell[j][i+1][2][1][1] = 1
pathCell[j][i+1][2]['color'] = color
end
pathCell[j][i+1][3][1][1] = 1
pathCell[j][i+1][3]['color'] = color
end
end
-- Thicken/Thin out paths
for n=1,#outpaths do
local i = outpaths[n][1]
local color = outpaths[n][2]
if i<r and pathCell[j][i][1][1][3]==1 and pathCell[j][i+1][1][1][1]==0 then
pathCell[j][i+1][1][1][1] = 0.5*pathCell[j][i][1][1][3]
pathCell[j][i+1][2][1][1] = 0.5*pathCell[j][i][2][1][3]
pathCell[j][i+1][1]['color'] = pathCell[j][i][1]['color']
pathCell[j][i+1][2]['color'] = pathCell[j][i][2]['color']
elseif i<r and pathCell[j][i][1][1][3]==0 and pathCell[j][i+1][1][1][1]==1 then
pathCell[j][i][1][1][3] = pathCell[j][i+1][1][1][1]
pathCell[j][i][2][1][3] = pathCell[j][i+1][2][1][1]
pathCell[j][i+1][1][1][1] = 0.5*pathCell[j][i][1][1][3]
pathCell[j][i+1][2][1][1] = 0.5*pathCell[j][i][2][1][3]
pathCell[j][i][1]['color'] = pathCell[j][i+1][1]['color']
pathCell[j][i][2]['color'] = pathCell[j][i+1][2]['color']
end
end
-- Thin double-in paths
for n=1,#inpaths do
local i = inpaths[n][1]
local color = inpaths[n][2]
if i<r and pathCell[j][i][3][1][3]==1 and pathCell[j][i+1][3][1][1]==1 and pathCell[j][i][3]['color']==pathCell[j][i+1][3]['color'] then
pathCell[j][i+1][3][1][1] = 0.5*pathCell[j][i][3][1][3]
end
end
end
for j=minc,c-1 do
for i=1,r-1 do
local straightpath=false
if (entries[j+1][i-1]==nil or (byes[j+1][entries[j+1][i-1]['headerindex']]) and isBlankEntry(j+1,i-1)) then
if (pathCell[j][i][3][1][3]~=0 and pathCell[j+1][i][1][1][3]~=0) or (pathCell[j][i+1][3][1][1]~=0 and pathCell[j+1][i+1][1][1][1]~=0) then
if pathCell[j+1][i][1][1][3]==pathCell[j+1][i][3][1][3] and pathCell[j+1][i+1][1][1][1]==pathCell[j+1][i+1][3][1][1] then
straightpath=true
end
pathCell[j+1][i][1][1][3]=pathCell[j][i][3][1][3]
pathCell[j+1][i+1][1][1][1]=pathCell[j][i+1][3][1][1]
pathCell[j+1][i][2][1][3]=pathCell[j][i][3][1][3]
pathCell[j+1][i+1][2][1][1]=pathCell[j][i+1][3][1][1]
entries[j+1][i-1]={['ctype']='line'}
entries[j+1][i]={['ctype']='blank'}
if notempty(entries[j+1][i+1]) then
entries[j+1][i+1]['ctype'] = 'line2'
else
entries[j+1][i+1]={['ctype']='line2'}
end
entries[j+1][i+2]={['ctype']='blank'}
if straightpath then
pathCell[j+1][i][3][1][3]=pathCell[j+1][i][1][1][3]
pathCell[j+1][i+1][3][1][1]=pathCell[j+1][i+1][1][1][1]
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-1 do
if pathCell[j][i][3][1][3]==1 or pathCell[j][i+1][3][1][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 noPaths(j-k,i) then
skipPath[j-k][i] = true
skipPath[j-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
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'}
entries[j][i+2]={['ctype']='line2'}
entries[j][i+3]={['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
local function categorize() -- new function added / Oot42 / 2025-01-04
local category
if isempty(fargs['nocat']) and mw.title.getCurrentTitle().namespace == 0 then
category = '[[Category:Pages using module Build bracket]]'
else
category = ''
end
return category
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 8 -- changed / Oot42 / 2025-01-04 / was 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
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 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')
:addClass('tbr bbr') -- added / Oot42 / 2025-01-04 / css classes for all brackets
:addClass(fargs.class) -- added / Oot42 / 2024-01-04 / optional parameter to add custom class(es)
: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','160px')) -- changed / Oot42 / 2025-01-04 / was 150px
if maxlegs[j]==0 then
tbl:tag('td'):css('width',getWidth('score','40px')) -- changed / Oot42 / 2025-01-04 / was 25px
else
for l=1,maxlegs[j] do
tbl:tag('td'):css('width',getWidth('score','40px')) -- changed / Oot42 / 2025-01-04 / was 25px
end
end
if aggregate and maxlegs[j]>1 then
tbl:tag('td'):css('width',getWidth('agg',getWidth('score','40px'))) -- changed / Oot42 / 2025-01-04 / was 25px
end
if j~=c then
if hascross[j] 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
if fargs.style then -- added / Oot42 / 2025-01-04 / optional parameter to add custom inline-styles
tbl:cssText(fargs.style)
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) .. categorize() -- changed / Oot42 / 2025-01-04 / add categorization function
end
return p