Модуль:RoundN (Bk;rl,&RoundN)

Перейти к навигации Перейти к поиску
Документация

Это модуль для предназначен для построения турнирных сеток различных соревнований.

Использование

[править код]

Основная команда {{#invoke:RoundN|main|columns=Х]}}, где Х — количество стадий турнира (3 стадии для 8 команд, 4 стадии для 16 команд и т. д.).

Альтернативная, сокращенная форма {{#invoke:RoundN|N16}} (или любая степень двойки от 2 до 512) эквивалентна {{#invoke:RoundN|main|columns = 4}}.

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

[править код]
Параметр Описание
|columns= Количество столбцов/стадий (3 стадии для 8 команд, 4 стадии для 16 и т. д.)

Примечание: Если значение параметра |columns= больше или равно 4 по умолчание отображается поле для игры за третье место. Если же |columns= менее 4 (от 2 до 8 команд) — по умолчанию скрыто.

{{{#}}} Неименованные параметры считываются последовательно группами по 5 таким образом, что:

Пример

{{#invoke:RoundN|main|columns=2
|День 1|A|'''7'''|B|5
|День 2|C|       |D|
|TDB   |A|       | |
}}
 
ПолуфиналФинал
 
      
 
День 1
 
 
A7
 
TDB
 
B5
 
A
 
День 2
 
 
 
C
 
 
D
 

Размещение каждой группы из 5 параметров на новой строке необязательно, но облегчает чтение. Рекомендуется добавлять в виде комментариев к коду наименования параметров <!-- Дата — Место|Команда 1|Счёт 1|Команда 2|Счёт 2 --> и стадию турнира <!-- Четвертьфинал / Полуфинал / Финал -->

|style= Установите параметр |style=, чтобы добавить пользовательский CSS в таблицу. {{#invoke:RoundN|main|columns=2|style=width:20em; font-size:70%}}
 
ПолуфиналФинал
 
      
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Чтобы выровнять весь блок по центру, используйте |style=margin:auto;

|scroll_height= Для больших таблиц установите |scroll_height= на желаемую высоту. {{#invoke:RoundN|main|columns=2|scroll_height=9em}}
 
ПолуфиналФинал
 
      
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ПолуфиналФинал
 
      
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Примечание Работает путём дублирования всей таблицы, а затем использования CSS для привязки клона таблицы к верхней части. Предположительно, для очень больших таблиц это может привести к значительному объёму дополнительного HTML-кода для загрузки по сравнению с использованием параметра |scroll_head_unlock=.

|scroll_head_unlock= Если необходимо, чтобы заголовок прокручивался вместе с таблицей (например, если требуется большая область просмотра), установите параметр |scroll_head_unlock=yes {{#invoke:RoundN|main|columns=2|scroll_height=9em|scroll_head_unlock=yes}}
 
ПолуфиналФинал
 
      
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
|skipmatchN= Если установлено, поля для N-й группы матчей отображаться не будут. Чаще всего используется для плей-офф или когда количество команд, играющих в первом раунде, не равно степени 2.

{{#invoke:RoundN|main|columns=2|skipmatch2=yes}}

 
ПолуфиналФинал
 
      
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Запись вида: |skipmatch=1-2;4;6-7 равнозначна записи:

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

Примечания:

  • Модуль не требует начальных нулей в параметре, то есть |skipmatch001= совпадает с |skipmatch1= (хотя это может облегчить чтение кода шаблона, если ввести соответствующее количество нулей);
  • 5 параметров, которые заполнили бы пропущенное поле, будут проигнорированы независимо от значения, если для параметра |omit_blanks= не установлено значение yes (см. ниже);
  • Ранее пропуск матча действовал только в первом раунде. Это ограничение больше не применяется.
|omit_blanks= Если для параметра |omit_blanks= установлено значение yes, то все параметры, которые были бы пропущены, вместо этого будут перенесены в следующее поле без пропусков. (По умолчанию это отключено, поскольку большинство шаблонов, созданных до выпуска этого модуля, должны были использовать пустые параметры в качестве заполнителей).
|bold_winner= Параметр bold_winner принимает значения high или low, что автоматически выделит жирным шрифтом текст с более высоким или более низким баллом соответственно. Другими словами, установите значение low, если выигрывает участник с более низким баллом, и high, если выигрывает участник с более высоким баллом.

Пример

{{#invoke:RoundN|main|columns=2|bold_winner=low
|День 1|A|7    |B|5
|День 2|C|7 (3)|D|5 (2)
|TDB   |B|     |D|
}}
 
ПолуфиналФинал
 
      
 
День 1
 
 
A7
 
TDB
 
B5
 
B
 
День 2
 
D
 
C7 (3)
 
 
D5 (2)
 

Примечания:

  • При вводе результата, который включает в себя не только (например: 7 (3)) модуль сначала удалит все нецифровые символы и объединит остальные. Так код 7 (3) и код 5 (2) будут преобразованы перед сравнением в 73 и 52, соответственно. Это должно быть верно для большинства случаев, однако вы можете переопределить значение, используя параметр manualboldmatchN;
  • Если значения равны или не содержат цифр, то ни одно из них не будет выделено жирным шрифтом;
  • Это не приводит к удалению уже существующего форматирования.
|manualboldmatchN= Аналогично параметрам |skipmatch= вы можете использовать |manualboldmatch= группой чтобы предотвратить автоматическое выделение жирным шрифтом (|manualboldmatch=1-2;4;6-7). Опять же, как и в случае со |skipmatch=, начальные нули могут быть добавлены по желанию.
|previewnumbers= Установите параметр |previewnumbers=yes, чтобы отображать номера рядом с каждой группой (полезно для |skipmatch= и |manualboldmatch=) при просмотре на странице шаблона. Обратите внимание, что эти цифры не будут отображаться в статье.
|RD##= Используйте |RD##=, заменив # на нужный столбец таким образом, чтобы 1 был крайним левым кругом, а X - крайним правым, |columns=X
{{#invoke:RoundN|N128
|RD2 = {{red|'''Второй раунд'''}}
|RD7 = {{red|'''Чемпионат'''}}
|RD8 = {{red|'''Прочее'''}}
|scroll_height=15em
}}
 
Раунд из 128Второй раундРаунд из 32Раунд из 16ЧетвертьфиналПолуфиналЧемпионат
 
                          
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Прочее
 
 
 
  
 
 
 
  
 
 
 
  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Раунд из 128Второй раундРаунд из 32Раунд из 16ЧетвертьфиналПолуфиналЧемпионат
 
                          
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Прочее
 
 
 
  
 
 
 
  
 
 
 
  
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Примечание: RD[N+1] = матч за третье место и = параметру |Consol=, если последний опущен, и наоборот RD[N+1] игнорируется, если параметр |Consol= заполнен. При этом RD[N+1] выглядит более интуитивно и предпочтительно.

|3rdplace= По умолчанию для |3rdplace= установлено значение yes, если |columns= больше 3, и no в противном случае.
|Consol= Установите параметр |Consol=name , чтобы изменить метку «Третье место» на «name». Рекомендуется формат RD#, где # = столбцы + 1. Данный параметр существует только для обеспечения совместимости со старыми шаблонами и признан неинтуитивным.
{{#invoke:RoundN|main|columns=1
|3rdplace=yes
|Consol=Второй финал
}}
 
Финал
 
  
 
 
 
 
 
 
 
 
 
 
 
 
 
Второй финал
 
 
 
 
 
 
 
 
 
 
|color= Включает подсветку призёров командой |color=yes
{{#invoke:RoundN|main|columns=1
|color=yes
|3rdplace=yes
||Команда А|1|Команда Б|2
||Команда В|3|Команда Г|4
}}
 
Финал
 
  
 
 
 
 
Команда А1
 
 
Команда Б2
 
 
 
 
 
Матч за 3-е место
 
 
 
 
 
Команда В3
 
 
Команда Г4
 

Примечание: Когда задано значение |color=yes, строки жёстко закодированы так, чтобы они были окрашены, как показано в примере выше. Чтобы раскрасить «правильные» ячейки, вы также должны активировать параметр |bold_winner= (см. выше). Таким образом, модуль сможет правильно идентифицировать и раскрасить победителей и проигравших.

{{#invoke:RoundN|main|columns=1
|color=yes
|bold_winner=high
|3rdplace=yes
||Команда А|1|Команда Б|2
||Команда В|3|Команда Г|4
}}
 
Финал
 
  
 
 
 
 
Команда А1
 
 
Команда Б2
 
 
 
 
 
Матч за 3-е место
 
 
 
 
 
Команда В3
 
 
Команда Г4
 
|color_repechage= Добавьте параметр |color_repechage=yes для случаев когда победитель финала награждается бронзовой медалью.
{{#invoke:RoundN|main|columns=1
|color_repechage=yes
|3rdplace=yes
||Команда А|1|Команда Б|2
||Команда В|3|Команда Г|4
}}
 
Финал
 
  
 
 
 
 
Команда А1
 
 
Команда Б2
 
 
 
 
 
Матч за 3-е место
 
 
 
 
 
Команда В3
 
 
Команда Г4
 

Примечания:

  • Когда задано значение |color_repechage=yes, строки жёстко закодированы так, чтобы они были окрашены, как показано в примере выше. Чтобы раскрасить «правильные» ячейки, вы также должны активировать параметр |bold_winner= (см. выше). Таким образом, модуль сможет правильно идентифицировать и раскрасить победителей и проигравших.
  • Когда задано значение |color_repechage=yes и название финальной секции переопределено, как в случаях, когда есть два бронзовых призёра, все видимые победители финального этапа будут окрашены в бронзовый цвет.
{{#invoke:RoundN|main|columns=3
|bold_winner     = high
|color_repechage = yes
|flex_tree       = yes
|skipmatch       = 2;4;7
|omit_blanks     = yes
|RD1             = Переигровка
|RD2             = Игры за бронзу
|RD3             = omit_label
|1|Команда А| 3|Команда Б| 2
|2|Команда В| 6|Команда Г| 5
|3|Команда Д| 8|Команда Е| 7
|4|Команда Ж|10|Команда З|11
}}
ПереигровкаИгры за бронзу
          
1
Команда А 3
3
Команда Б 2
Команда Д 8
Команда Е 7
2
Команда В 6
4
Команда Г 5
Команда Ж10
Команда З11
|team-width= При необходимости установите параметр |team-width= на желаемую ширину. (по умолчанию 170px). Менять ширину данного столбца без особой необходимости не рекомендуется так как она подбирается автоматически.
|score-width= При необходимости установите параметр |score-width= на желаемую ширину. (по умолчанию 30px) Менять ширину данного столбца без особой необходимости не рекомендуется так как она подбирается автоматически.
|widescore= Значение |widescore=yes в основном равно |score-width=40. Предусмотрено для совместимости. Игнорируется, если задан параметр |score-width=.
|score-boxes= Установите в параметре |score-boxes= желаемое количество полей для подсчета очков за матч. (по умолчанию 1). За номером может следовать + sum, что добавит еще одно поле для подсчёта очков к сумме всех остальных.
{{#invoke:RoundN|main|columns=3
|score-boxes     = 3+ sum
|bold_winner     = high
|color_repechage = yes
|flex_tree       = yes
|skipmatch       = 2;4;7
|omit_blanks     = yes
|RD1             = Переигровка
|RD2             = Игры за бронзу
|RD3             = omit_label
|1|Команда А| 3| 3| 3|Команда Б| 2| 2| 2
|2|Команда В| 6| 6| 6|Команда Г| 5| 5| 5
|3|Команда Д| 8| 8| 8|Команда Е| 7| 7| 7
|4|Команда Ж|10|10|10|Команда З|11|11|11
}}
ПереигровкаИгры за бронзу
                   
1
Команда А 3 3 39
3
Команда Б 2 2 2 6
Команда Д 8 8 824
Команда Е 7 7 7 21
2
Команда В 6 6 618
4
Команда Г 5 5 5 15
Команда Ж10101030
Команда З111111 33
|flex_tree= Установите параметр |flex_tree=yes, чтобы сделать скобки по вертикали более компактными. То есть, чтобы между матчами одного раунда оставалось меньше места.
|short_brackets= Установите параметр |short_brackets=yes, чтобы сделать скобки более компактными по горизонтали. То есть, чтобы между матчами следующих раундов оставалось меньше места.
{{#invoke:RoundN|main|columns=3
|short_brackets = yes
|flex_tree      = yes
|score-boxes    = 3+ sum
|bold_winner    = high
|skipmatch      = 2;4;7
|omit_blanks    = yes
|RD1            = Переигровка
|RD2            = Игры за бронзу
|RD3            = omit_label
|1|Команда А| 3| 3| 3|Команда Б| 2| 2| 2
|2|Команда В| 6| 6| 6|Команда Г| 5| 5| 5
|3|Команда Д| 8| 8| 8|Команда Е| 7| 7| 7
|4|Команда Ж|10|10|10|Команда З|11|11|11
}}
ПереигровкаИгры за бронзу
                   
1
Команда А 3 3 39
3
Команда Б 2 2 2 6
Команда Д 8 8 824
Команда Е 7 7 7 21
2
Команда В 6 6 618
4
Команда Г 5 5 5 15
Команда Ж10101030
Команда З111111 33

Недокументированные параметры

[править код]

Обратите внимание, что некоторые из этих функций, возможно, не были задокументированы из-за неполной реализации.

  • |no_column_head=
  • |branch_upwards=

См. также

[править код]
local p = {
	RD = {'Четвертьфинал', 'Полуфинал', 'Финал', 'Матч за 3-е место'
	},
-- Цвета текста и фона сопряжены, и когда вы устанавливаете один, вы должны установить другой (специальные возможности)
	textColor = {
		head = 'var(--color-base, #202122)', '#202122', '#202122', '#202122', 'var(--color-base, #202122)'
	},
	bgColor = {
		head = 'var(--background-color-neutral, #eaecf0)', 'gold', 'silver', '#c96', 'var(--background-color-neutral-subtle, #f8f9fa)'
	},
	reuseStr = {},
	saveStr = function(self, name, ...)
		if not self.reuseStr[name] then
			self.reuseStr[name] = table.concat{...}
		end
		return self.reuseStr[name]
	end
}

-- Обеспечивает удобное сокращение вплоть до {{#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]	-- чтобы сделать регистр нечувствительным
end

-- Экономит память и позволяет избежать ошибок при использовании nil в качестве таблицы, предоставляя временную таблицу;
-- при использовании nil как false; используйте 'table(k)' для поиска таблицы[k]
p.nilAsTab = {
	__index = function(t, i)
		return setmetatable({}, setmetatable(p.nilAsTab, {__index = {t = t, i = i}}))
	end,
	__newindex = function (pt, pi, v)	-- сохраняем новые значения в постоянной таблице, а не во временной
		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'Модуль: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

local 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

local 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 var(--border-color-base, #a2a9b1)',
			['background-color'] = p.bgColor.head,
			color = p.textColor.head
		}
	end
end

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

local 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.previewnumbers and mw.clone(p.dpBoxBase) or 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
}

local 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

local 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 var(--border-color-base, #a2a9b1)', ['background-color'] = p.bgColor[4], color = p.textColor[4]}
local 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
-- обратная совместимость (wikitemplates bold each arg individually)
			local hasBold, b = tostring(v):gsub("([^']*)'''([^']*)", '%1<b>%2</b>')
			if b == 1 then
				v = hasBold
			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
		local text = v or f[1] and '' or '&nbsp;'
		if f.bold then
			text = mw.ustring.gsub(text, '(%(%[%[[^%[%]]*%]%]%))', '<span style="font-weight:normal">%1</span>')
		end
		tab.r = rowNum[r]:tag'td'
:css(p.teamBoxCSS)
:css(cell)
:attr{rowspan = 2}
:node(mw.html.create(f.bold and 'b'):wikitext(text))
	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', 'color', 'color_repechage', '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.namespace = mw.title.getCurrentTitle().namespace
	p.previewnumbers = p.namespace ~= 0 and p.previewnumbers
	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%;border-collapse:separate;', 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 = { -- ширина столбцов по умолчанию
		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
	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 ('Раунд из ' .. math.pow(2, p.tCols - k + 1))
			drawHead(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})
			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.color_repechage = p['color_repechage'] and ((c == p.tCols) or ((c == p.tCols - 1) and skipMatch[math.pow(2, p.tCols) - 1]))
		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) * (3 + p.scoreBoxes)}
								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)
						if p.previewnumbers then
							rowNum[m.r].nodes[# rowNum[m.r].nodes]
:tag'div'
:css{float = 'left', border = '1px solid red', padding = '0 .5ex', ['color'] = 'red'}
:wikitext(m.num)
:attr{title = 'Номер виден только за пределами пространства статьи (например, в шаблонах) если есть параметр |numberpreview=yes'}
						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
						if col.color_repechage then
							col.color_repechage = 2
						end
						if p.bold then
							if m.bold.win(m.phase) and (colorFinal or col.color_repechage) then
								color_index = 1 + (col.show3rd or 0) + (col.color_repechage or 0)
							elseif m.bold.box[#m.nonEmpty] then
								color_index = 2 + (col.show3rd or 0) + (col.color_repechage or 0)
							else
								color_index = 4
							end
							p.teamBoxCSS = (colorFinal or col.color_repechage) and {
								border = p.teamBoxNormal.border, ['background-color'] = p.bgColor[color_index], color = p.textColor[color_index]
							} or p.teamBoxNormal
						else
							p.teamBoxCSS = (colorFinal or col.color_repechage) and {
								border = p.teamBoxNormal.border, ['background-color'] = p.bgColor[m.phase + (col.show3rd or 0) + (col.color_repechage or 0)],
								color = p.textColor[m.phase + (col.show3rd or 0) + (col.color_repechage or 0)]
							} or p.teamBoxNormal
						end
						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 p.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
			col.t2 = nil
			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 --draws the ']' when a PAIR of matches needs lines
						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 --draws the lines when only top OR bottom match need lines
						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
function p.main(frame, columns)
	local args = require'Модуль: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
	local 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 -0.7ex'):format(padding, padding),
		float = 'left',
		['background-color'] = p.bgColor.head,
		border = '1px solid var(--border-color-base, #a2a9b1)',
		color = p.textColor.head,
		['text-align'] = 'center',
		width = width}
:wikitext(arg(1, '&nbsp;'))
end

return p