Модуль:RoundN

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку
{{i}} Документація модуля[перегляд] [редагувати] [історія] [очистити кеш]

Використання

[ред. код]

Цей модуль використовується для меташаблонів таких як {{Round16}}, {{Round8}} тощо. Хоча початково розроблявся для спорту, він може використовауватися, щоб представляти будь-яке дерево.

Проста форма:

{{#invoke:RoundN|main|columns = [кількість колонок (3 колонки для Round8, 4 колонки для Round16 тощо)]}}

Альтернативно, скорочена форма {{#invoke:RoundN|N16}} (або будь-який ступінь 2 між N2 та N512) відповідає {{#invoke:RoundN|main|columns = 4}}

Список параметрів

[ред. код]
  • червоні назви параметрів є обов'язковими.
  • назви параметрів курсивом обов'язкові в певних випадках
  • сині назви параметрів залучають наво можливості, що не були доступні до виходу цього модуля
Назва параметра Опис

columns

columns
Number of columns/eliminations (3 columns for Round8, 4 columns for Round16, etc.)

Note

  • For columns less than 4 (i.e. Round2-Round8), the 3rd Place match box is hidden by default. For columns greater or equal to 4, the 3rd Place match box is visible by default. This reflects the behavior of the templates prior to this module's release.

shorthand

Shorthand

While required for invoking the main function ({{#invoke:RoundN|main|columns=}}), this module has meta functions in the form of 'N##' which can call main with the right columns for you. For example, you may replace ... |main|columns = 7... with ...|N128 .... Valid for N# where # is a power of 2. You must invoke this module directly to use this (vs. a template that already has a columns value). Only works up to N512. If say N1024 ever becomes necessary, call main directly and set |columns=10.

PS: Though adding shorthands up to N ~70 trillion would be easy, it would make unnecessary overhead as these meta functions are generated eached time this module is called (granted generating such functions up to ~70 trillion is probably less expensive than making the table for columns = 3).

{{{#}}}

{{{#}}}
Unnamed parameters (i.e. a value not prefixed by [param_name] =) are read sequentially in groups of 5 such that:

Приклад 1

[ред. код]
{{#invoke:RoundN|main|columns=2
|Day 1|A|'''7'''|B|5
|Day 2|C||D|
|tdb|A|||
}}
 
ПівфіналиФінал
 
      
 
Day 1
 
 
A7
 
tdb
 
B5
 
A
 
Day 2
 
 
 
C
 
 
D
 

Placing each group of 5 on a new line is optional, but does make it easier to read. Also, consider adding the comments such as <!-- Date-Place/Team 1/Score 1/Team 2/Score 2 --> on top and <!-- Finals --> above the first group of 5 in the finals round, etc.

style

style
Set the style parameter to add custom CSS to the table.

Обговорення модуля:RoundN/testcases/1

To center-align the whole block, use style=margin:auto;

scroll_height

scroll_height
For large tables, set scroll_height to the desired height in pixels.

Обговорення модуля:RoundN/testcases/1

CSS units are also allowed (i.e. '20em', '30%', etc.).

Note This works by duplicating the entire table and then using CSS to lock the clone of the table to the top of the div. Conceivably, for extremely large tables, this can result in a significant amount of extra HTML code to download versus if scroll_head_unlock is used.

scroll_head_unlock

scroll_head_unlock
If it is desirable to have the round heading scroll with the table (such as if a larger viewing area is desired), set scroll_head_unlock to 'yes'

Обговорення модуля:RoundN/testcases/1

skipmatch##

skipmatch##
If set, boxes for the #th match group will not be shown. Most often used for playoffs or when the number of teams playing in the first round is not a power of 2.

Обговорення модуля:RoundN/testcases/1

shorthand (range)

NEW:

The shorthand |skipmatch = 1-2;4;6-7 will do the same thing as:

|skipmatch1=yes
|skipmatch2=yes
|skipmatch4=yes
|skipmatch6=yes
|skipmatch7=yes

Note:

  • Unlike in the original templates, this module does not require leading zeroes in this parameter, i.e. skipmatch001 is the same as skipmatch1 (though it may make your template code easier to read if lead with an appropriate number of zeroes).
  • The 5 parameters that would have populated the skipped box will be ignored regardless of value unless omit_blanks is set to 'yes' (see below).
  • Previously, skipmatch only worked in the first round. This limitation no longer applies. (See Module talk:RoundN/testcases/3)

omit_blanks

omit_blanks
If omit_blanks is set to yes, then all parameters that would have been skipped will instead be shifted to the next non-skipped box. (This is turned off by default because most templates made before the release of this module were required to use empty parameters as placeholders.)

Обговорення модуля:RoundN/testcases/1

bold_winner

bold_winner
The bold_winner parameter accepts either 'high' or 'low', which will automatically bold the text of the participant with the higher or lower score, respectively. In other words, set this to 'low' if the lower score wins and 'high' if the high score wins.

Приклад 2

[ред. код]

Обговорення модуля:RoundN/testcases/2 Note:

  • If entering a score that includes non-numbers
    such as 3 (6), the Semi Final score for team C in the above example
    the module will first remove all non-digit characters and concatenate the rest. For example, 3 (5) and 3 (6) would be converted to 35 and 36, respectively, before being compared. This should be valid for most cases, however, you may override using the manualboldmatch## parameter.
  • If the scores are tied or contain no numbers, then neither will be bolded, however, you may still manually bold them with wikimarkup.
  • This does not remove any formatting already present.

manualboldmatch##

manualboldmatch##

Обговорення модуля:RoundN/testcases/2


shorthand (range)
Shorthand

The form manualboldmatch = 1-3;6;9-12 is also available.

previewnumbers

previewnumbers

Set |previewnumbers = yes to show numbers next to each match group (useful for skipmatch and manualboldmatch) when viewing on the template page.

Note that these numbers will not appear in article space.

RD##

RD##
Use RD#, replacing # with the desired column such that 1 is the leftmost round and X is the rightmost when columns = X. For example:
{{#invoke:RoundN|N128
|RD2 = {{red|'''SECOND ROUND'''}}|RD7 = {{red|'''Championship'''}} | RD8 = {{red|'''So close, yet so far'''}}
|scroll_height=15em
}}
 
Round of 128SECOND ROUNDRound of 321/8 фіналуЧвертьфіналиПівфіналиChampionship
 
                          
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 So close, yet so far
 
 
 
  
 
 
 
  
 
 
 
  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Round of 128SECOND ROUNDRound of 321/8 фіналуЧвертьфіналиПівфіналиChampionship
 
                          
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 So close, yet so far
 
 
 
  
 
 
 
  
 
 
 
  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Note RD[N+1] = Third Place, and will perform the job of the Consol parameter if the latter is omitted, i.e. RD[N+1] is ignored if Consol is true. Also, this alternate name for Consol was not available prior to the release of this module (and is provided because the module's programmer thought 'Consol' was unintuitive).

3rdplace

3rdplace
By default, |3rdplace= is set to 'yes' when columns is greater than 3 and 'no' otherwise. Override as desired. (See Consol if you wish to rename this heading)
{{#invoke:RoundN|main|columns=1
|1
}}
 
Фінал
 
  
 
1
 
 
 
 
 
 
 

Consol

Consol
Set Consol=name to change the 'Third Place' label to 'name'. You may also use the form RD# where # = columns + 1.
{{#invoke:RoundN|main|columns=1
|3rdplace=yes
|Consol=Runner up
|1
}}
 
Фінал
 
  
 
1
 
 
 
 
 
 
 
 
 
 
 
Runner up
 
 
 
 
 
 
 
 
 
 

color

color
Add |color=yes
 
Фінал
 
  
 
7
 
 
Team A3
 
 
Team B2
 
 
 
 
 
Фінал
 
 
8
 
 
Team C
 
 
Team D0
 
  • Note: When |color=yes is set, the brackets are hardcoded to be colored as shown in the example above. To color the "correct" cells, you must also activate bold_winner to either high or low. Thus allowing the module to identify and color the winners and losers correctly.

Add |color=yes & |bold_winner=high for colored brackets when the winner is the highest scorer.

 
Фінал
 
  
 
7
 
 
Silver medalist2
 
 
Gold medalist3
 
 
 
 
 
Фінал
 
 
8
 
 
 0
 
 
Bronze medalist1
 

Add |color=yes & |bold_winner=low for colored brackets when the winner is the lowest scorer.

 
Фінал
 
  
 
7
 
 
Gold medalist2
 
 
Silver medalist3
 
 
 
 
 
Фінал
 
 
8
 
 
Bronze medalist0
 
 
 1
 

color_repechage

color_repechage
Add |color_repechage=yes for repechage brackets, where the winner is awarded a bronze medal.
 
Фінал
 
  
 
7
 
 
Team A3
 
 
Team B2
 
  • Note 1: When |color_repechage=yes is set, the brackets are hardcoded to be colored as shown in the example above. To color the "correct" cells, you must also activate bold_winner to either high or low. Thus allowing the module to identify and color the winners and losers correctly.
  • Note 2: When |color_repechage=yes is set AND the final match of the brackest is skipped, as in cases where there are two bronze medalists, all winners of the final stage visible will be colored bronze.

Add |color_repechage=yes & |bold_winner=high for colored brackets when the winner is the highest scorer.

 
Фінал
 
  
 
7
 
 
Team A3
 
 
Team B2
 

Add |color_repechage=yes & |bold_winner=low for colored brackets when the winner is the lowest scorer.

 
Фінал
 
  
 
7
 
 
Team A3
 
 
Team B2
 

Add |color_repechage=yes & skip the final match of the brackets for colored brackets when there are two bronze medalists.

RepechageBronze medal
          
1
Team A3
3
Team B2
Team A8
Team E7
2
Team C6
4
Team D5
Team C10
Team F11

team-width

team-width
Set team-width to the desired width in pixels. (Default is 170)

score-width

score-width
Set score-width to the desired width in pixels. (Default is 30)

widescore

widescore
Setting |widescore=yes is basically equal to |score-width=40. Provided for compatibility. Ignored if score-width is set.

score-boxes

score-boxes
Set score-boxes to the desired number of score boxes per match. (Default is 1). The number can be followed by + sum, which will add one more score box with the sum of all the others. For examples, see test case 5 and test case 6.

flex_tree

flex_tree
Set |flex_tree=yes to make the brackets vertically more compact. That is, to have less space between matches of the same round.
ПівфіналиФінал
      
 
 
 
 
 
 

short_brackets

short_brackets
Set |short_brackets=yes to make the brackets horizontally more compact. That is, to have less space between matches of following rounds.
 
ПівфіналиФінал
 
      
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Вузлові (node) функції

[ред. код]

For greater customization, node functions may be added to the parameter usually used for providing match information such as dates and location. Simply insert node_function{function name} in the appropriate location. A demonstration of most of the node functions can be found at RoundN/testcases/4.

  • orphan - Current node is disconnected from all other nodes (no lines will be drawn). One practical use for this may be when the winners of the previous round advance to a different tournament instead of playing each other but the losers still play a consolation match for 3rd place, as per this discussion.

For the next three node functions, the parameters usually used for team names and scores should be omitted if these node functions are used.

  • line - Current node is omitted and replaced with a horizontal line. Text may be displayed above this line via node_function{line(text)} (at some point, the preferred format would be node_function{line}text to match the form of the canvas function though backwards compatibility would probably be maintained). If curly brackets are desired in the text, make sure to escape them with \.
  • bridge - Current node is omitted and replaced with a vertical line.
  • canvas - Current node is replaced with whatever you want via node_function{canvas}anything
  • heading - Not yet implemented. Planned node function with the form node_function{heading(name)}date/location. Makes a round name heading like for the Consol parameter, but may be placed anywhere, as per the "Elimination Rounds" of RoundN/testcases/6 (which currently renders the planned output of this function without the functions).

Незадокументовані можливості

[ред. код]

Alas User:Codehydro seriously overbuilt this module and never found the time to document even half the features available. Check out the following examples which may contain some advanced features:


Частковий перелік незадокументованих параметрів

[ред. код]
  • no_column_head
  • branch_upwards

Звернуть увагу, що деякі з цих можливостей можуть бути недокументуванні через неповноціність своєї імплементації.

local p = {
	RD = {
		'1/8 фіналу',
		'Чвертьфінали',
		'Півфінали',
		'Фінал',
		'за 3-тє місце'
	},
	bgColor = {head = '#f2f2f2', 'gold', 'silver', '#C96', '#f9f9f9'},
	reuseStr = {},
	saveStr = function(self, name, ...)
		if not self.reuseStr[name] then
			self.reuseStr[name] = table.concat{...}
		end
		return self.reuseStr[name]
	end
}

--Provides a convenient naming shortcut up to {{#invoke:RoundN|N512}} = {{invoke:RoundN|main|columns = 9}}
for columns = 1, 9 do
	local N = math.pow(2, columns)
	p['N' .. N] = function(frame)
		return p.main(frame.args, columns)
	end
	p['n' .. N] = p['N' .. N]--to make case insensitive
end

--saves memory and avoids errors when using a nil as a table by providing a temporary table; if using nil as false; use 'table(k)' to look up table[k]
p.nilAsTab = {
	__index = function(t, i)
		return setmetatable({}, setmetatable(p.nilAsTab, {__index = {t = t, i = i}}))
	end,
	__newindex = function (pt, pi, v) --store new values in actual table rather than temporary
		rawset(p.nilAsTab.t, p.nilAsTab.i, {})[p.nilAsTab.i][pi] = v
		setmetatable(p.nilAsTab.t[p.nilAsTab.i], {__call = p.nilAsTab.__call})
	end,
	__call = function(t, i)
		return t and rawget(t, i)
	end
}
--never assign a value to these or they will stop being empty
local infiniteEmpty = setmetatable({}, {__index = setmetatable({}, p.nilAsTab), p.nilAsTab}) -- infiniteEmpty[1][2][3]...[infinity] = {}
local callableEmpty = setmetatable({}, p.nilAsTab)

local rowNum, head, m, col, tab, esc = {}, {}, {num = 1, phase = 0, bold = infiniteEmpty}, {}, mw.html.create'table', {
	bs = require'Module:Escape',--backslash
	comma = {['(%([^,]*),([^%)]*%))'] = '%1|@!#|%2'},--escape commas in ()
}
local nodeFunc = {
	scanPattern = function(self, args, step)
		self.pattern = nil
		if args[step] then
			self.pattern, self.nonFunc = string.match(esc.bs:text(args[step]), '^node_function{(.-)}(.*)')
		end
		if self.pattern then
			for k, v in pairs(esc.comma) do
				self.pattern = self.pattern:gsub(k, v)
			end
			self.nonFunc = self.nonFunc and esc.bs:undo(self.nonFunc)
			self.pattern = mw.text.split(self.pattern, '%s*,%s*')
			for k, v in ipairs(self.pattern) do
				local func, arg = string.match(v, '^(%w+)%(?([^%)]*)')
				if func and self[func] and self[func].main then
					self.pattern[k] = func
					if arg then
						for x, y in pairs(esc.comma) do
							arg = esc.bs:undo(arg):gsub(y:gsub('%%%d', ''), x:match('%)([^%(])%(') or x:gsub('\\', ''))
						end
						self[func].arg = self[func].arg or {}
						self[func].arg[m.num] = arg
					end
				end
			end
		end
		return self.pattern
	end,
	helper = {
		topBranch = function()--node is top of fork if top is 0
			return (m.num - col.top) % 2
		end,
		addText = function(text)
			if text and text ~= '' then
				tab.r:wikitext(text)
			end
		end
	},
	line = {--this node is omitted and replaced with a line
		main = function(x)
			local h = p.getNodeFunc()
			if m.available then
				local text, topId, isTop, notTop = h.line.arg[m.num] or '', h.topBranch()
				isTop = topId == 0
				notTop = {[isTop and 1 or 0] = p.reuseStr.solid}
				for k = 0, 1 do
					tab.r = rowNum[m.r + k * 4]:tag'td'
						:css(notTop[k] and
							{[isTop and 'border-top' or 'border-bottom'] = notTop[k]}
							or {}
						)
						:attr{
							rowspan = ({[0] = 4, 2})[k],
							colspan = p.colspan
						}
					h.addText(text or h.nonFunc)
					text = nil
				end
				m.available = false
			else
				return nil
			end
			return x
		end
	},
	bridge = {--Draw a line to the neighboring node in the same column that is not connected to the current node
		main = function(x)
			local h = p.getNodeFunc()
			h.bridge.lay[col.c][m.num - col.top + 1 + (h.topBranch() == 1 and 1 or -1)] = true
			h.addText(nonFunc)
			return x
		end,
		lay = setmetatable({}, p.nilAsTab)
	},
	canvas = {--Merges all cells in node. Content will be the next parameter.
		main = function(x)
			local h = p.getNodeFunc()
			if m.available then
				tab.r = rowNum[m.r]:tag'td'
					:attr{
						rowspan = 6,
						colspan = p.colspan
					}
				h.addText(h.nonFunc)
				m.available = false
				return x
			else
				return nil
			end
		end
	},
	orphan = {--sets a flag for skipMatch to be set by p._main
		main = function(x)
			p.getNodeFunc().orphan.num = m.num
			return x
		end
	},
	skipAllowed = {--table of supported node functions when node is skipped (i.e. by skipmatch)
		bridge = true,
		canvas = true
	}
}

setmetatable(nodeFunc.helper, {__index = nodeFunc})
function p.getNodeFunc()
	return nodeFunc.helper
end

function newRow(bodyRow)
	local first = p.flex_tree.merge and mw.clone(p.flex_tree.cell) or p.flex_tree.cell
	tab.r = tab:tag'tr'
		:node(first)
	if bodyRow then
		table.insert(rowNum, bodyRow, tab.r)
		if p.flex_tree.merge then
			rowNum[bodyRow].first = first
			rowNum[bodyRow].first.unchanged = true
		end
	end
end

function drawHead(text, row3rd)
	local td = (row3rd and rowNum[row3rd]:tag'td':attr{rowspan = 2}
		or head.row:tag'td')
		:attr{colspan = p.colspan}
	if text ~= 'omit_label' then
		td:wikitext(text):css{
			['text-align'] = 'center',
			border = '1px solid #aaa',
			background = p.bgColor.head
		}
	end
end

function spacer(width)
	tab.r:tag'td'
		:attr{width = width}
		:wikitext(p.no_column_head and '' or '&nbsp;')
end

function dpBox(v, r)
	p.dpBoxBase = p.dpBoxBase or mw.html.create'td':attr{rowspan = 2, colspan = p.colspan}
	if not v then
		p.dpBoxEmpty = p.dpBoxEmpty or mw.clone(p.dpBoxBase):wikitext(p.flex_tree.wt)
		rowNum[r]:node(p.dpBoxEmpty)
	else
		rowNum[r]:node(mw.clone(p.dpBoxBase):wikitext(v))
	end
end

p.scoreWasher = {
	numberFormat = '%-?%d+%.?%d*',
	main = function (self, s)
		if s then
			for _, cycle in ipairs(self.cycles) do
				s = s:gsub(unpack(cycle))
			end
			if p.scoreSumBox and self.plus then
				local t = 0
				for _, part in ipairs(mw.text.split(s, self.plus)) do
					t = t + (tonumber(part:match('%-?%d+%.?%d*')) or 0)
				end
				return t
			end
			return tonumber(s:match(self.numberFormat)) or math.huge
		end
		return 0
	end,
	spin = function(self, v)
		table.insert(self, v)	
		return self
	end,
	load = function (self, cycle)
		local wash, rinse = 0, {spin = self.spin}
		for v in cycle:gfind('%(([^%(%)]-)%)') do
			if v == '_plus_' then
				self.plus = v
				rinse:spin(v)
				cycle = cycle:gsub('%(_plus_%)', '', 1)
			else
				wash = wash + 1
				rinse:spin('%'):spin(wash)
			end
		end
		table.insert(self.cycles, {esc.bs:undo(cycle, '%%'), table.concat(rinse)})
	end,
	init = function(self, setting)
		self.cycles = {original = setting}
		for cycle in (setting and esc.bs:text(setting) or '{<.->} {[^%d]*}'):gfind('{(.-)}') do
			self:load(cycle)
		end
	end,
		sum = function (clean)
		local sum = {0, 0}
		for _, box in ipairs(clean) do
			for team, score in ipairs(box) do
				sum[team] = sum[team] + score
			end
		end
		return unpack(math.max(unpack(sum)) == math.huge and {'&mdash;', '&mdash;'} or sum)
	end
}

function boldWin(s1, s2)
	return setmetatable(
		p.bold and s1 ~= s2 and (math[({'min', 'max'})[p.bold]](s1, s2) == s1 and {true} or {[2] = true}) or callableEmpty,
		p.nilAsTab
	)
end

function maxSpan(span, start, rows)
	return math.min(span, math.max(0, rows - start + 1))
end

--in case of templates like RDseed need padding value
p.teamBoxPadding = function()
 return '.6ex'
end
p.teamBoxPadTab = {padding = '0 ' .. p.teamBoxPadding()}
p.teamBoxNormal = {border = '1px solid #aaa', background = p.bgColor[4]}
function teamBox(v, r, f)
	if p.flex_tree.merge and not v and f.phase == 2 then
		for i = -2, 0 do
			if rowNum[r + i].first.unchanged then
				rowNum[r + i].first.unchanged = nil
				rowNum[r + i].first:node(p.unflex_div)
			end
		end
		tab.r:attr{rowspan = 4}:css{['vertical-align'] = 'center'}
	else
		if not p.bold then
		--backwards compatability (wikitemplates bold each arg individually)
			local hasBold, b = tostring(v):gsub("([^']*)'''([^']*)", '%1%%s%2')
			if b == 1 then
				v = hasBold:format('<b>')
			end
		end
		local cell
		if f[1] then
			cell = f.sumBox and f.sumBox[1] and
				{padding = f.sumBox[1]}
				or {['border-left'] = f.borderLeft}
			cell['text-align'] = v and f[1]
		else
			cell = p.teamBoxPadTab
		end
		tab.r = rowNum[r]:tag'td'
			:css(p.teamBoxCSS)
			:css(cell)
			:attr{rowspan = 2}
			:node(mw.html.create(f.bold and 'b'):wikitext(v or f[1] and '' or '&nbsp;'))
	end
end

function p._main(args)
	function args:clean(key, params)--prevent html comments from breaking named args and reduces repeat concatenation
		params = params or {}
		local clean = args[key] or params.ifNil
		if clean then
			params.append = params.append or ''
			clean = mw.text.decode(clean):gsub('<!%-.-%->', ''):gsub(params.pattern or '[^%w-;%.]', '') .. params.append
			clean = clean ~= params.append and clean or params.ifNil
		end
		args[key] = params.keepOld and args[key] or clean
		return clean
	end
	p.cols = tonumber(args:clean('columns', {pattern = '%D'}))
	p.tCols = (tonumber(args:clean('final_RDs_excluded', {pattern = '%D'})) or 0) + p.cols
	local matchPer = {
		pattern = '%d*per%d+[%-x]%d+',
		vals = '(%d*)per(%d+)([%-x])(%d+)'
	}
	local skipMatch, unBold  = {}, {}--(skip|manualbold)match# to boolean
	for k, _ in pairs(args) do
		local mType, mNum = string.match(k, '^(%l+)match(%d*)$')
		mType, mNum = ({skip = skipMatch, manualbold = unBold})[mType], tonumber(mNum)
		if mType then 
			if mNum then
				mType[mNum] = args:clean(k) == 'yes' or args[k] == 'true'
			else
				for pattern in args:clean(k, {ifNil = ''}):gfind(matchPer.pattern) do
					local d1, period, op, d2 = pattern:match(matchPer.vals)
					d1 = tonumber(d1) or 1
					d2 = op == '-' and d2 or (d1 + period * (d2 - 1))
					for y = d1, d2, period do
						mType[y] = true
					end
				end
				for _, x in ipairs(mw.text.split(args[k]:gsub(matchPer.pattern, ''):gsub('[;%-%a][;%-%a]+', ';'):match('^;*(.-)[;%-]*$'), ';')) do
					x = mw.text.split(x, '-')
					for y = tonumber(x[1]) or 1, tonumber(x[2] or x[1]) or 0 do
						mType[y] = true
					end
				end
			end
		end
	end
	for _, v in ipairs({--more args to boolean
		'widescore',
		'template',
		'article_include',
		'color',
		'3rdplace',
		'omit_blanks',
		'scroll_head_unlock',
		'previewnumbers',
		'flex_tree',
		'no_column_head',
		'short_brackets',
		'branch_upwards'
	}) do
		if args[v] and (p[v] == nil or type(p[v]) == 'boolean') then
			p[v] = args:clean(v) == 'yes' or args[v] == 'true'
		end
	end
	p.scoreWasher:init(args['score-clean'])
	p.scoreWasher.demo = args.demoWash and tonumber(args:clean('demoWash', {pattern = '%D'}), 10)
	p.scoreSumBox = args['score-boxes'] and args['score-boxes']:match('%d ?%+ ?sum')
	p.bold = ({low = 1, high = 2})[args:clean('bold_winner')] or p.scoreSumBox and 2
	local sumBox = p.scoreSumBox and 1 or 0
	p.scoreBoxes = (tonumber(args:clean('score-boxes', {pattern = '%D'})) or 1) + sumBox
	p.scoreSumBox = p.scoreBoxes > 0 and p.scoreSumBox or nil
	local boxStyle = p.scoreBoxes > 1 and
		(p.scoreSumBox and
			setmetatable(
				{{}, [p.scoreBoxes] = {'0 1ex'}},
				{__call = function(t, i) if t[i] then return nil end return 0 end}
			)
			or setmetatable(
				{},
				{__call = function() return 0 end}
			)
		)	
		or setmetatable({}, {__call = function() return nil end})
	p.colspan = p.scoreBoxes > 0 and (p.scoreBoxes + 1) or nil
	local nodeArgs = {
		score = p.scoreBoxes - sumBox,
		team = {offset = 1 + p.scoreBoxes - sumBox}
	}
	nodeArgs.all = 1 + nodeArgs.team.offset * 2
	nodeArgs.tableSum = {
		__add = function(v, t)
			if #t == 3 then
				return v + nodeArgs.all
			end
			local s = v
			for i, n in ipairs(t) do
				s = s + n
			end
			return s--[[ + (p.scoreSumBox and #t == 3 and -2 or 0) --merging disabled with score boxes, uncomment if enable]]
		end
	}
	nodeArgs.team[1] = 1--constant to be replaced later by new param
	nodeArgs.team[2] = nodeArgs.team[1] + nodeArgs.team.offset
	nodeArgs.blank = setmetatable({}, nodeArgs.tableSum)
	p.unflex_div = mw.html.create'div'
					:css{overflow = 'hidden', height = '1ex'}
					:wikitext'&nbsp;'
	p.flex_tree = setmetatable({},{__index = {
		merge = p.flex_tree and p.scoreBoxes == 0,
		wt = p.flex_tree and '' or '&nbsp;',
		cell = mw.html.create'td'
			:node(not p.flex_tree and p.unflex_div or nil)
	}})
	if args:clean'scroll_height' then
		local fontSize, fontUnit = args.style and args.style:match('font%-size *: *(%d+)([^ ]+)')
		if fontSize then
			local units = {
				em = 1,
				ex = 2,
				['%'] = 0.01
			}
			fontSize, fontUnit = {fontSize * fontUnit}
		end
	end
	tab
		:cssText(table.concat{args.scroll_height and 'padding' or 'margin', ':', fontSize and (math.ceil(fontSize * 10) / 10) or '.9', 'em 2em 1em 1em;border:0;', fontSize and '' or 'font-size:90%;', args.style})
		:attr{cellpadding = 0, cellspacing = 0}
	if not p.no_column_head then--headings row
		newRow()
		head.row = tab.r
			:css{['white-space'] = args.scroll_height and 'nowrap'}
		newRow()
	else 
		tab.r = tab:tag'tr'
		tab.r:tag'td'
	end
	local sp = {--set column widths
		args['team-width'] or 170,
		p.widescore and 40 or 30,
		p.short_brackets and 6 or 15,
		p.short_brackets and 4 or 20
	}
	local scoreWidth = args:clean('score-width', {pattern = '[^%d;]'}) and mw.text.split(args['score-width'], ';') or {}
	scoreWidth[1] = tonumber(scoreWidth[1], 10)
	if p.scoreSumBox and #scoreWidth ~= 1 then
		local _scoreWidth = {}
		for k = 1, p.scoreBoxes - 1 do
			_scoreWidth[k] = tonumber(scoreWidth[k], 10) or math.ceil(sp[2] * 0.75)
		end
		setmetatable(scoreWidth, _scoreWidth)
	end
	if p.template or p.article_include then
		p.template = mw.title.new(args.name)
		p.templateFixedName = (p.template.namespace == 0 and not p.article_include and 'Template:' or '') .. p.template.fullText
	end
	p.template = p.template and mw.title.new(args:clean('name', {pattern = ''}))
	local head_br = {
		count = 0,
		compare = function (self, text)
			if text and args.scroll_height then
				local _, count = text:gsub('<br[ >/]', '%1')
				self.count = math.max(self.count, count)
			end
			return text
		end
	}
	p.branch_upwards = p.branch_upwards and 0
	for k = 1, p.cols do
		if k > 1 then
			spacer(sp[3])
			spacer(sp[4])
			if not p.no_column_head then
				head.row:tag'td':attr{colspan = 2}
			end
		end
		spacer(sp[1])
		for s = 1, p.scoreBoxes do
			spacer(#scoreWidth == 1 and scoreWidth[1] or scoreWidth[s] or sp[2])
		end
		if not p.no_column_head then
			head.wt = head_br:compare(args:clean('RD' .. k, {pattern = ''}))
				or p.RD[#p.RD + k - p.tCols - 1]
				or ('Round of ' .. math.pow(2, p.tCols - k + 1))
			drawHead(
				k == 1 and p.template and mw.getCurrentFrame():expandTemplate{
					title = 'tnavbar-header',
					args = {head.wt, p.templateFixedName}
				} or head.wt
			)
		end
	end
	sp.row = tab.r
	col.tot = math.pow(2, p.tCols - 1)
	local step, bump, bumpBase, rows = 1, 0, mw.html.create'td':attr{colspan = p.colspan}, col.tot * 6--Begin body row output
	args.line_px = table.concat{args:clean('line_px') or 3, args.line_px ~= '0' and 'px' or nil}
	tab.line = {--reduces concats and 'or' statements
		{
			[true] = args.line_px,
			[false] = 0
		},
		args.line_px:rep(2):gsub('(%a)(%d)', '%1 %2', 1)
	}
	p['3rdplace'] =  p.tCols == p.cols and (p['3rdplace'] or p.cols > 3 and nil == p['3rdplace'] and not p.no_column_head)
	if p['3rdplace'] then
		p.textThird = args.Consol or args['RD' .. (p.cols + 1)] or p.RD[4]
		local no3rdText = p.no_column_head or p.textThird and p.textThird:match('omit_label')
		rowNum.third = math.max(math.pow(2, p.branch_upwards and -3 or p.cols - 2) * 9 + (no3rdText and 4 or 9), no3rdText and 12 or 17, rows)
	end
	for r = 1, rowNum.third or rows do
		newRow(r)
	end
	p:saveStr('solid', tab.line[1][true], ' solid')
	p.cornerDiv = mw.html.create'div':css{height = tab.line[1][true], ['border-right'] = p.reuseStr.solid}
	for c = 1, p.cols do
		col.c = c
		local bumps = bump
		if c > 1 then
			col.tot = col.tot + math.pow(2, p.tCols - c)
			if p.branch_upwards then
				bumps = 0
				rowNum[1]:tag'td':attr{rowspan = 4}
			else
			rowNum[1]:node(c < p.cols and
				mw.clone(bumpBase):attr{rowspan = bump}
				or p.no_column_head and p.template and
					mw.html.create'td':wikitext(mw.getCurrentFrame():expandTemplate{
						title = 'tnavbar-header',
						args = {'', p.templateFixedName}
					})
			)
			end
		end
		col.top = m.num
		p.span = p.tCols > c and bump * 2 or p.branch_upwards or math.max((bump - 1) / 2, 2)
		col.show3rd = p['3rdplace'] and c == p.tCols and rowNum.third
		local colorFinal, bumpMid = p.color and c == p.tCols, p.span > 0 and mw.clone(bumpBase):attr{rowspan = p.span} or nil
		for r = 1, col.show3rd or rows, 2 do
			m.r = r + bumps
			if col.show3rd or rowNum[m.r] and m.num <= col.tot then
				if m.phase == 0 then
					m.showBox = setmetatable({1, nodeArgs.team.offset, nodeArgs.team.offset}, nodeArgs.tableSum)
					if nodeFunc:scanPattern(args, step) then
						nodeFunc.called = {}
						m.available = true
					else
						m.available = nil
					end
				end
				if skipMatch[m.num] then
					if m.phase == 0 then
						if nodeFunc.pattern then
							for x, y in ipairs(nodeFunc.pattern) do
								if nodeFunc.skipAllowed[y] then
									nodeFunc.called[y] = nodeFunc[y].main(x)
								end
							end
						end
						local canvas = nodeFunc.pattern and nodeFunc.called.canvas and 6
						rowNum[m.r + (canvas or 0)]:tag'td':attr{rowspan = maxSpan((canvas and 0 or 6) + bump * 2, m.r + (canvas or 0), rows), colspan = p.colspan}
					elseif m.phase == 2 then
						if nodeFunc.pattern and (nodeFunc.called.bridge or nodeFunc.called.canvas) then
							step = step + 1
						end
						m.num = m.num + 1
						step = step + (p.omit_blanks and 0 or m.showBox)
						bumps = bumps + (col.show3rd and 0 or maxSpan(p.span, m.r, rows))
					end
				elseif m.phase == 0 then
					if nodeFunc.pattern then
						for x, y in ipairs(nodeFunc.pattern) do
							if nodeFunc[y] and nodeFunc[y].main then
								nodeFunc.called[y] = nodeFunc[y].main(x)
							end
						end
						if m.available == false then
							m.showBox = nodeArgs.blank
							step = step + 1
						end
					end
					if m.showBox[1] then
						if col.show3rd then
							col.show3rd = (m.num - col.top) * 2
							if col.show3rd == 2 then
								if p.textThird:match('omit_label') then
									p.textThird = nil
								end
								if rowNum[rows + 1] and p.cols > 1 then --if 3rd place extends below bottom cell
									rowNum[rows + 1]:tag'td':attr{
										rowspan = m.r + 9 - rows - (text and 0 or 2),
										colspan = (p.cols - 1) * 4
									}
								end
								if p.tCols == 1 then
									bumps = p.textThird and 3 or 0
								elseif p.branch_upwards then
									r = 7
									bumps = p.textThird and 2 or 0
								end
								m.r = r + bumps
								if p.textThird then
									drawHead(p.textThird, m.r)
									bumps = bumps + 2
									m.r = r + bumps
								end
							end
						end
						dpBox(nodeFunc.pattern and nodeFunc.nonFunc or args[step], m.r, skipMatch[m.num])
						if p.previewnumbers then
							p.namespace = p.namespace or mw.title.getCurrentTitle().namespace
							if p.namespace ~= 0 then
								tab.r:tag'div'
									:css{
										float = 'left',
										border = '1px solid red',
										padding = '0 .5ex',
										['color'] = 'red'
									}
									:wikitext(m.num)
									:attr{title = 'Number only visible outside article space (e.g. template) when |numberpreview=yes'}
							end
						end
					end
					if p.colspan then
						m.nonEmpty = {}
						for s = step + 2, step + nodeArgs.team.offset do
							local i = {s, s + nodeArgs.team.offset}
							if args[i[1]] or args[i[2]] then
								table.insert(m.nonEmpty, i)
							end
						end
						if p.bold and m.showBox[2] and m.showBox[3] and not unBold[m.num] then
							m.bold = {
								box = {},
								clean = {}
							}
							local notSummed = not p.scoreSumBox or #m.nonEmpty < 2
							for s, i in ipairs(m.nonEmpty) do
								m.bold.clean[s] = {p.scoreWasher:main(args[i[1]]), p.scoreWasher:main(args[i[2]])}
								m.bold.box[s] = notSummed and boldWin(m.bold.clean[s][1], m.bold.clean[s][2]) or callableEmpty
							end
							if p.scoreSumBox and m.nonEmpty[2] then
								local i = {-step, -step - 1}
								table.insert(m.nonEmpty, i)
								args[i[1]], args[i[2]] = p.scoreWasher.sum(m.bold.clean)
								m.bold.box[p.scoreBoxes] = boldWin(args[i[1]], args[i[2]])
							end
							getmetatable(boxStyle).__index = p.scoreSumBoxes and {[#m.nonEmpty] = boxStyle[p.scoreBoxes]}
							m.bold.win = m.bold.box[#m.nonEmpty] or callableEmpty
						else
							m.bold = infiniteEmpty
						end
					end
				else
					if m.showBox[m.phase] then
						p.teamBoxCSS = colorFinal and 
							{border = p.teamBoxNormal.border, background = p.bgColor[m.phase + (col.show3rd or 0)]}
							or p.teamBoxNormal
						local f = {phase = m.phase, bold = m.bold.win(m.phase)}
						teamBox(args[step + nodeArgs.team[m.phase]], m.r, f)
						f[1] = 'center'
						if p.colspan then
							if m.nonEmpty[1] then
								local loneSum
								if #m.nonEmpty < p.scoreBoxes then
									loneSum = #m.nonEmpty == 1 and boxStyle[p.scoreBoxes]
									tab.r:attr{colspan = 1 + p.scoreBoxes - #m.nonEmpty}
								end
								for s, i in ipairs(m.nonEmpty) do
									f.borderLeft = boxStyle(s)
									f.sumBox = loneSum or boxStyle[s]
									f.bold = m.bold.box[s](m.phase)
									teamBox(args[i[m.phase]], m.r, f)
								end
							else
								for s = 1, p.scoreBoxes do
									f.borderLeft = boxStyle(s)
									teamBox(nil, m.r, f)
								end
							end
						end
					end
					if m.phase == 2 then
						col.show3rd = col.show3rd ~= 2 and col.show3rd or nil
						if p.scoreWasher.demo and p.scoreWasher.demo == m.num and mw.title.getCurrentTitle().namespace ~= 0 then
							table.insert(m.bold.clean, 1, {args[step + nodeArgs.team[1]], args[step + nodeArgs.team[2]]})
							return table.concat{
								'Score data for match specified by <code>|demoWash=</code>:<br>',
								mw.dumpObject{scores = m.bold.clean, cycles = p.scoreWasher.cycles, sum = p.scoreSumBox and {m.nonEmpty[#m.nonEmpty][1], m.nonEmpty[#m.nonEmpty][1]}},
								'<table>',
								tostring(sp.row), '<tr>',
								tostring(rowNum[m.r - 4]), '<tr>',
								tostring(rowNum[m.r - 2]), '<tr>',
								tostring(rowNum[m.r]), '</table>',
							}
						end
						if nodeFunc.orphan.num == m.num then
							skipMatch[m.num] = 'orphan'
						end
						step = step + m.showBox
						m.num = m.num + 1
						if bump > 0 and rowNum[m.r + 2] and not (nodeFunc.pattern and nodeFunc.called.canvas) then
							bumps = bumps + p.span
							rowNum[m.r + 2]:node(bumpMid)
						end
						r = r + (col.show3rd or bump)
					end
				end
				m.phase = (m.phase + 1) % 3
			end
		end
		if p.cols > c then--draw lines to next round
			p.unit = bump + 3
			bump = 3 * math.pow(2, c) - 3
			bumps = p.branch_upwards and 4 or (p.unit + 1)
			rowNum[1]
				:tag'td':attr{rowspan = bumps}
			if not p.branch_upwards then
				rowNum[1]:tag'td'
					:attr{rowspan = (p.branch_upwards or bump) + 4}
					:css(nodeFunc.bridge.lay[c](0) and
						{['border-right'] = p.reuseStr.solid}
						or {}
					)
			end
			col.n = 0
			for r = bumps + 1, rows, p.unit * 2 do
				tab.r = rowNum[r]:tag'td'
				local interval = ((r - bumps - 1) / (p.unit * 2)) % 4
				if interval % 2 == 0 then
					--col.t and col.t2 control whether lines are drawn
					col.t = col.t2 or skipMatch[col.tot + col.n / 2 + 1] and 3 or ((skipMatch[col.top] and 1 or 0) + (skipMatch[col.top + 1] and 2 or 0))
					col.n = col.n + 2
					col.t2 = skipMatch[col.tot + col.n / 2 + 1] and 3 or ((skipMatch[col.top + col.n] and 1 or 0) + (skipMatch[col.top + col.n + 1] and 2 or 0))
					if col.t == 0 then
						tab.r
							:attr{rowspan = maxSpan(p.unit * 2, r, rows)}
							:css(skipMatch[col.tot + col.n / 2] and {} or {
								border = p.reuseStr.solid,
								['border-left'] = 0
							})
					else
						tab.r
							:attr{rowspan = maxSpan(p.unit, r, rows)}
							:cssText(col.t == 2 and 
								p:saveStr('topRight', 'border-width:', tab.line[2], ' 0 0;border-style:solid')
								or col.t == 1 and (nodeFunc.bridge.lay[c](col.n - 2) and
									p:saveStr('right', ';border-right:', p.reuseStr.solid)
									or 'vertical-align:bottom'
								)
								or nil
							)
							:node(col.t == 1 and interval > 0 and not nodeFunc.bridge.lay[c](col.n - 2) and p.cornerDiv)
						rowNum[r + (p.branch_upwards and (4 - bump) or p.unit)]:tag'td'
							:attr{rowspan = maxSpan(p.unit, r + p.unit, rows)}
							:cssText(col.t == 1 and
								p:saveStr('bttmRght', 'border-width:0 ', tab.line[2], ' 0;border-style:solid')
								or col.t == 2 and (nodeFunc.bridge.lay[c](col.n + 2) and
									p:saveStr('right', ';border-right:', p.reuseStr.solid)
									or 'vertical-align:top'
								)
								or nil
							)
							:node(col.t == 2 and interval ~= 2 and not nodeFunc.bridge.lay[c](col.n + 2) and p.cornerDiv)
					end
					col.t = {
						col.t < 3,
						rowNum[r + p.unit * 5] and col.t2 < 3 or false
					}
					rowNum[r + (p.branch_upwards or p.unit)]:tag'td'
						:attr{rowspan = maxSpan(p.unit * 4, r + (p.branch_upwards and (4 - bump) or p.unit), rows)}
						:css(interval == 0 and (col.t[1] or col.t[2]) and {
							['border-width'] = table.concat{tab.line[1][col.t[1]], ' 0 ', tab.line[1][col.t[2]]},
							['border-style'] = 'solid'
						} or {})
				else
					tab.r
						:attr{rowspan = maxSpan(p.unit * 2, r, rows)}
						:css(nodeFunc.bridge.lay[c](col.n) and
							{['border-right'] = p.reuseStr.solid}
							or {}
						)
				end
			end
		end
	end
	local lock_height = (head_br.count or 0) + 1
	return args.scroll_height and
		mw.html.create'div'
			:cssText'border-bottom:1px solid #eee;display:inline-block'
			:node(not (p.scroll_head_unlock or p.no_column_head) and mw.html.create'div'
				:css{
					overflow = 'hidden',
					height = lock_height * 1.4 + 1.6 .. 'em',
					['border-bottom'] = 'inherit',
					['margin-right'] = '17px'
				}
				:node(mw.clone(tab))
			)
			:tag'div'
				:css{
					['overflow-y'] = 'scroll',
					['max-height'] = tonumber(args.scroll_height, 10) and args.scroll_height .. 'px' or args.scroll_height
				}
				:node(not (p.scroll_head_unlock or p.no_column_head) and
					tab:css{['margin-top'] = math.floor(-10 * (lock_height * 1.4 + 1.6)/(fontSize or .9)) / 10 .. 'em', ['padding-top'] = '-3px'}
					or tab
				)
			:done()
		or tab
end

--[[local standard = {
	'beta' = {
		bold_winner = 'high',
		omit_blanks = 'yes',
		auto_3rd = 'yes'
	}
}--]]
function p.main(frame, columns)
	local args = require'Module:Arguments'.getArgs(frame, {trim = false})
	args.columns = args.columns or columns
	return p._main(args)
end

function p.seed(frame)
	local parent = frame:getParent() or frame
	function arg(k, alt)
		return parent.args[k] or frame.args[k] or alt
	end
	local padding, width = arg(2, p.teamBoxPadding()), arg(3, arg('widescore') and 40 or 30)
	padding = tonumber(padding) and tonumber(padding) .. 'px' or padding
	width = tonumber(width) and tonumber(width) .. 'px' or width
	return mw.html.create'div'
		:css{
			margin = ('-1px %s -1px -%s'):format(padding, padding),
			float = 'left',
			['background-color'] = p.bgColor.head,
			border = '1px solid #aaa',
			['text-align'] = 'center',
			width = width
		}
		:wikitext(arg(1, '&nbsp;'))
end

return p