Модуль:Sports table
Материал из вики «Вече»
Дополнительные действия
Для документации этого модуля может быть создана страница Модуль:Sports table/doc
-- Строит турнирную таблицу: места команд, их показатели, столбец результатов
-- с заливкой из палитры, статусы, примечания и нижний колонтитул.
-- Набор и оформление столбцов показателей задаёт подмодуль стиля, выбираемый параметром
-- style: [[Модуль:Sports table/WDL]] (по умолчанию), [[Модуль:Sports table/WDLHA]],
-- [[Модуль:Sports table/WDL OT]], [[Модуль:Sports table/WL]], [[Модуль:Sports table/WL OT]].
-- Общие данные и функции семейства лежат в [[Модуль:Sports table/sub]]. Параметры проверяет
-- [[Модуль:Sports table/argcheck]], согласованность чисел — [[Модуль:Sports table/totalscheck]],
-- таблицу из результатов матчей собирает [[Модуль:Sports table/auto]]. Шахматку — внутри
-- таблицы, под ней или вместо неё (show_matches) — строит [[Модуль:Sports results]].
-- Вызывается из шаблонов через #invoke (p.main) и из [[Модуль:Sports group]] (p._main).
-- Документация и примеры вызова: [[Модуль:Sports table/doc]].
require('strict')
local p = {}
local p_sub = require('Модуль:Sports table/sub')
-- [[Модуль:Yesno]] понимает «да» и «нет» наравне с «yes» и «no», а нераспознанное значение
-- превращает в nil, так что мусор в логическом параметре не выдаёт себя за «да».
local yesno = require('Модуль:Yesno')
-- Обращений к mw.ustring много, в том числе в циклах, а локальная переменная в Lua
-- доступна быстрее, чем поле глобальной таблицы.
local ustring = mw.ustring
-- Данные --------------------------------------------------------------------------------
-- Подписи столбца результатов для кодов в res_col_header; любое другое значение выводится в заголовке как есть.
local results_header = {
Q = 'Квалификация',
QR = 'Квалификация или выбывание',
P = 'Повышение',
PQR = 'Повышение, квалификация или выбывание',
PR = 'Повышение или выбывание',
PQ = 'Повышение или квалификация',
R = 'Выбывание'}
-- Русские буквы статуса, выводимые вместо латинских, если не задано
-- keep_english_status_labels=yes. Буквы, которых здесь нет, выводятся как есть.
local rus_letters = {A = 'С', C = 'Ч', D = 'Д', E = 'В', G = 'Г', H = 'Х', O = 'П/О', P = 'П', Q = 'К', R = 'В', S = 'П/С', T = 'Н'}
-- Разбор параметров ---------------------------------------------------------------------
-- Разбирает параметр-переключатель и возвращает true или false. Нераспознанное значение
-- действует как незаданное — берётся default. Иначе show_positions=скрыть скрывал бы
-- столбец мест, хотя по умолчанию тот показывается, — параметр срабатывал бы вопреки замыслу редактора.
-- Такие значения отмечает отслеживающей категорией [[Модуль:Sports table/argcheck]].
-- У переключателей карточки матча (p.flag в [[Модуль:Football box/base]]) правило другое:
-- там любое непустое нераспознанное значение — истина. Разница намеренная: у aet и подобных
-- нет осмысленного «по умолчанию», а в разметке в них часто пишут пояснение вместо «да».
local function flag(value, default)
local yn = yesno(value)
if yn == nil then
yn = yesno(default)
end
return yn or false
end
-- Разбирает режим таблицы. only_pld_pts=yes оставляет из показателей только сыгранные
-- матчи и очки (full_table = false); only_pld_pts=no_hide_class_rules оставляет таблицу
-- полной, но скрывает правила классификации. Возвращает full_table, hide_results,
-- hide_footer, show_class_rules.
local function resolve_table_mode(Args)
local full_table = true
local hide_results = yesno(Args['hide_results'] or 'no')
local hide_footer = yesno(Args['hide_footer'] or 'no')
local pld_pts_val = string.lower(Args['only_pld_pts'] or 'no')
local show_class_rules = flag(Args['show_class_rules'], 'yes')
if yesno(pld_pts_val) then
full_table = false
elseif pld_pts_val == 'no_hide_class_rules' then
full_table = true
show_class_rules = false
end
return full_table, hide_results, hide_footer, show_class_rules
end
-- Раскладывает team_order — коды команд через «;» или «,» — в team1, team2 и далее,
-- начиная с номера team_order_start (по умолчанию 1). Пишет прямо в Args;
-- пустой элемент номер занимает, но ничего не записывает.
local function apply_team_order(Args)
if not Args['team_order'] or Args['team_order'] == '' then
return
end
local team_order_offset = (tonumber(Args['team_order_start']) or 1) - 1
local tlist = mw.text.split(Args['team_order'], '%s*[;,]%s*')
for k, tname in ipairs(tlist) do
if tname ~= '' then
Args['team' .. (k + team_order_offset)] = tname
end
end
end
-- Собирает team_list из team1, team2… начиная с позиции, следующей за N_teams.
-- Счёт идёт до первого пропущенного номера либо до bottom_pos (lowest_pos), если она не меньше top_pos.
-- При show_totals=yes добавляет в Args и team_list строку итогов — псевдокоманду
-- ppp.total_row_code, суммы в которую записывают подмодули стиля (ppp.add_to_totals).
-- Возвращает N_teams — номер последней позиции, total_row_name и show_totals.
local function build_team_list(Args, team_list, top_pos, bottom_pos, N_teams)
while Args['team' .. N_teams + 1] ~= nil and (bottom_pos < top_pos or N_teams < bottom_pos) do
N_teams = N_teams + 1
-- team_list двусторонний: по номеру позиции — код команды, по коду — номер позиции
-- (по нему находятся showteam и команда, на примечание которой ссылается другая).
team_list[N_teams] = Args['team' .. N_teams]
team_list[Args['team' .. N_teams]] = N_teams
end
local total_row_name = p_sub.total_row_code
local show_totals = flag(Args['show_totals'], 'no')
if show_totals then
N_teams = N_teams + 1
Args['team' .. N_teams] = total_row_name
Args['name_' .. total_row_name] = 'Всего'
Args['result' .. N_teams] = total_row_name
-- Цвет задаётся именем из палитры, а не значением: проверка параметров сверяет цвета
-- по именам, и шестнадцатеричный код собственного вывода модуля отметила бы как цвет вне палитры.
Args['col_' .. total_row_name] = 'neutral'
team_list[N_teams] = Args['team' .. N_teams]
team_list[Args['team' .. N_teams]] = N_teams
end
return N_teams, total_row_name, show_totals
end
-- Дописывает title_source к title либо, если такой пары нет, table_header_source
-- к table_header. Возвращает true, если источник дописан: тогда build_footer не ставит
-- в колонтитул пометку «нет источника».
local function apply_title_source(Args)
if Args['title'] and Args['title_source'] then
Args['title'] = Args['title'] .. Args['title_source']
return true
elseif Args['table_header'] and Args['table_header_source'] then
Args['table_header'] = Args['table_header'] .. Args['table_header_source']
return true
end
return false
end
-- Возвращает baselink — страницу, из которой вызван модуль, — и template_name — цель
-- ссылок навбара (build_vte_text). baselink пуст, если вызов идёт через [[Модуль:Excerpt]]
-- или прямо со страницы, на которой выводится таблица. template_name задаётся одноимённым
-- параметром, по умолчанию — «:baselink#якорь таблицы». Ручной baselink применяется после
-- template_name и на него не влияет.
-- p._main берёт только template_name: ссылки на отчёты о матчах, в том числе с учётом
-- ручного baselink, ведёт ppp.resolve_report_links.
local function resolve_baselink(frame, currentTitle, Args, table_anchor)
local baselink = frame:getParent():getTitle()
if baselink == 'Модуль:Excerpt' then
baselink = ''
end
if currentTitle.fullText == baselink then
baselink = ''
end
local template_name = Args['template_name'] or (baselink ~= '' and (':' .. baselink .. (table_anchor ~= '' and '#' .. table_anchor or ''))) or ''
local baselink_override = Args['baselink']
if baselink_override and baselink_override ~= '' then
baselink = baselink_override
if currentTitle.fullText == baselink then
baselink = ''
end
end
return baselink, template_name
end
-- Выводимые строки ----------------------------------------------------------------------
-- Возвращает первую и последнюю выводимые позиции. Выборка из n_to_show строк (show_limit)
-- строится вокруг команды ii_show (showteam); без ii_show, а также при show_limit не меньше
-- числа позиций, не больше 1 или дробном выводится вся таблица. Команда у края таблицы даёт
-- первые или последние n_to_show строк, в середине — поровну строк выше и ниже неё, то есть
-- 2·⌊n_to_show/2⌋ + 1: при чётном show_limit на строку больше.
local function compute_display_range(top_pos, N_teams, ii_show, n_to_show)
local ii_start, ii_end
local check_n = ((n_to_show >= (N_teams - top_pos + 1)) or (n_to_show <= 1) or (n_to_show ~= math.floor(n_to_show)))
if check_n or (not ii_show) then
ii_start = top_pos
ii_end = N_teams
else
-- Строк по каждую сторону от ii_show.
local n_show_side = math.floor(n_to_show / 2)
if (ii_show - top_pos + 1) <= n_show_side then
-- У верхнего края: первые n_to_show строк.
ii_start = top_pos
ii_end = top_pos + n_to_show - 1
elseif ii_show >= (N_teams + 1 - n_show_side) then
-- У нижнего края: последние n_to_show строк.
ii_start = N_teams + 1 - n_to_show
ii_end = N_teams
else
-- В середине: n_show_side строк выше и ниже.
ii_start = ii_show - n_show_side
ii_end = ii_show + n_show_side
end
end
return ii_start, ii_end
end
-- Делит выводимые строки на группы для столбца результатов: подряд идущие строки с одним
-- кодом результата (или без него) объединяются в одну ячейку с rowspan, а splitN обрывает
-- группу после позиции N. Возвращает таблицу по номеру первой строки группы:
-- {n = число строк, result = код результата или ''}.
local function result_groups(Args, result_by_pos, ii_start, ii_end)
local groups = {}
local ii = ii_start
while ii <= ii_end do
local result_local = result_by_pos[ii] or ''
local n = 1
while ii + n <= ii_end and not Args['split' .. (ii + n - 1)]
and (result_by_pos[ii + n] or '') == result_local do
n = n + 1
end
groups[ii] = {n = n, result = result_local}
ii = ii + n
end
return groups
end
-- Заливка ячеек -------------------------------------------------------------------------
-- Возвращает класс заливки для утверждённого имени палитры — « st-green1» и т. п. —
-- либо пустую строку. Ведущий пробел нужен: имя дописывается к уже имеющемуся классу внутри
-- одного атрибута class, а двух атрибутов class в теге быть не может.
-- Цвет утверждённого имени и его тёмный вариант лежат в таблице стилей, поэтому такое имя
-- выводится классом. Произвольный цвет выводится встроенным стилем (resolve_bg_col) и класса
-- не получает: ссылки в такой ячейке тёмная тема находит по самому атрибуту style.
local function filled_class(Args, respre, result_local)
if not result_local then
return ''
end
local col_key = Args[respre .. 'col_' .. result_local]
if not col_key or col_key == '' then
return ''
end
local bg_col, origin = p_sub.palette_color(col_key)
if not bg_col or bg_col == 'inherit' then
return ''
end
-- Имя класса совпадает с именем цвета, поэтому переименование цвета в палитре требует
-- правки и таблицы стилей — они и так меняются вместе.
if origin == 'named' then
return ' st-' .. mw.ustring.lower(mw.text.trim(col_key))
end
return ''
end
-- Возвращает встроенный стиль заливки ячейки по коду результата result_local: без кода —
-- прозрачный фон, без цвета для кода (col_<код>) или с нераспознанным цветом — inherit.
-- Для утверждённого имени палитры возвращает пустую строку: цвет тогда задаёт класс, см. filled_class.
-- С непрозрачным фоном всегда явно задаётся и цвет текста (ppp.text_color). Встроенный style
-- в тёмной теме не инвертируется: фон остаётся светлым, а унаследованный цвет текста становится светлым —
-- выходит светлый текст на светлом фоне. Для transparent и inherit цвет текста остаётся наследуемым:
-- такой фон меняется вместе с темой.
local function resolve_bg_col(Args, respre, result_local)
if not result_local then
return 'background-color:transparent; color:inherit;'
end
local col_key = Args[respre .. 'col_' .. result_local]
if not col_key or col_key == '' then
return 'background-color:inherit; color:inherit;'
end
-- Цвет применяется в любом написании, в том числе шестнадцатеричным кодом: так он задан
-- во множестве статей, и отказ оставил бы их без заливки. Написания вне палитры отмечает
-- отдельной категорией проверка параметров.
local bg_col, origin = p_sub.palette_color(col_key)
if not bg_col or bg_col == 'inherit' then
return 'background-color:inherit; color:inherit;'
end
-- Утверждённое имя палитры выводится классом (filled_class): у класса есть вариант для
-- тёмной темы, а встроенный стиль правилом CSS не перебить иначе как через !important на каждый цвет.
if origin == 'named' then
return ''
end
-- Произвольный цвет известен только здесь, и в таблице стилей его держать негде:
-- он остаётся встроенным и варианта для тёмной темы не получает.
return 'background-color:' .. bg_col .. '; color:' .. p_sub.text_color .. ';'
end
-- Части вывода --------------------------------------------------------------------------
-- Возвращает навбар шаблона template_name ([[Модуль:Navbar]], _ruwikiNavbar) с отбивкой
-- либо пустую строку, если template_name пуст.
local function build_vte_text(template_name, Args)
if template_name == '' then
return ''
end
local VTE_text = ' ' .. require('Модуль:Navbar')._ruwikiNavbar({template_name,})
-- В сортируемой таблице (sortable_table=yes) ссылки навбара не срабатывают —
-- ошибка движка, см. обсуждение:
-- https://en.wikipedia.org/w/index.php?oldid=832717047#Sortable_link_disables_navbar_links?
-- Обходится она снятием тегов abbr: они заменяются пробелами.
if flag(Args['sortable_table'], 'no') then
VTE_text = ustring.gsub(VTE_text, '<%/?abbr[^<>]*>', ' ')
end
return VTE_text
end
-- Возвращает respre — префикс полей результата, — текст ячейки заголовка столбца
-- результатов и notes_exist. При result_prefix=X поля результата читаются с префиксом:
-- X_resultN, X_col_<код>, X_text_<код>, X_res_col_header, X_status_<код>.
-- Примечание к заголовку (note_header_res) выводится только в полной таблице и выставляет notes_exist.
local function build_results_header(Args, full_table, frame, notes_exist)
local respre = (Args['result_prefix'] or '') .. '_'
respre = (respre == '_') and '' or respre
local res_col_header_key = Args[respre .. 'res_col_header']
local local_res_header = results_header[res_col_header_key] or res_col_header_key or ''
local res_head_note = Args['note_header_res']
local res_head_note_text = ''
if full_table and res_head_note then
notes_exist = true
res_head_note_text = frame:expandTemplate{title = 'efn',
args = {group = 'lower-alpha', res_head_note}
}
end
local results_header_txt = '! scope="col" |' .. local_res_header .. res_head_note_text .. '\n'
return respre, results_header_txt, notes_exist
end
-- Строит шапку таблицы: столбцы стиля (p_style.header) и, если шахматка выводится внутри
-- таблицы, разделительный столбец и столбцы шахматки (p_matches.header).
-- Возвращает массив строк разметки и число столбцов.
local function build_header(p_style, t, Args, pos_label, group_col, VTE_text, full_table, results_header_txt, match_table, p_matches, N_teams, top_pos, team_list, legs)
local t_return = p_style.header(t, Args, pos_label, group_col, VTE_text, full_table, results_header_txt)
if match_table then
-- Ячейка над разделительным столбцом пуста, и атрибута scope у неё нет —
-- иначе скринридер объявил бы её заголовком.
t_return.count = t_return.count + 1
table.insert(t_return.tab_text, '! class="unsortable st-spacer"| \n')
t_return = p_matches.header(t_return, Args, N_teams, top_pos, team_list, legs)
end
return t_return.tab_text, t_return.count
end
-- Строит обозначение статуса команды из букв status_local (например, «PQ»): буквы через
-- запятую, каждая через letter_wrapper, в элементе st-status (полужирный) — в скобках
-- после названия либо с тире перед ним, по t_status.position; перед названием буквы
-- переводятся в строчные (string.lower действует только на латиницу). Знаки, которых нет
-- в t_status.code, пропускаются. Выведенные буквы отмечаются в t_status.called — по этим
-- отметкам build_footer собирает легенду. Без единой известной буквы возвращает пустую строку.
local function build_status_string(t_status, status_local, letter_wrapper)
local status_string = ''
if not status_local then
return status_string
end
local status_let_first = true
for jjj = 1, ustring.len(status_local) do
local curr_letter = ustring.upper(ustring.sub(status_local, jjj, jjj))
if t_status.code[curr_letter] then
if status_let_first then
status_string = letter_wrapper(curr_letter)
status_let_first = false
else
status_string = status_string .. ', ' .. letter_wrapper(curr_letter)
end
t_status.called[curr_letter] = true
end
end
if not status_let_first then
if t_status.position == 'before' then
status_string = '<span class="st-status">' .. string.lower(status_string) .. ' –</span> '
else
status_string = ' <span class="st-status">(' .. status_string .. ')</span>'
end
end
return status_string
end
-- Открывает строку таблицы и добавляет в t её первые ячейки: место (при position_col),
-- группу (в полной таблице при group_col) и название команды со статусом и сноской.
-- Строка итогов получает класс sortbottom, который держит её внизу при сортировке; место у неё пусто.
-- Класс заливки ставится каждой ячейке, а не строке. Строкой было бы короче, но при
-- show_matches=yes в ту же строку дописываются клетки шахматки, и заливка по коду результата
-- просвечивала бы через них: клетки матчей оказались бы закрашены цветом квалификации.
-- Снять её там нельзя — прозрачная ячейка как раз показывает фон строки.
local function append_row_lead_cells(t, show_totals, team_code_ii, total_row_name, position_col, ii_fw, bg_col, bg_class, pos_num, full_table, group_col, group_txt, t_status, status_string, team_name, note_string)
if show_totals and team_code_ii == total_row_name then
t[#t + 1] = '|- class="sortbottom"\n'
else
t[#t + 1] = '|- \n'
end
if position_col then
-- У строки итогов места нет: это не участник, а сумма по таблице.
local pos_text = (show_totals and team_code_ii == total_row_name) and '' or pos_num
t[#t + 1] = '| class="st-pos' .. bg_class .. '" style="' .. ii_fw .. bg_col .. '"| ' .. pos_text .. '\n'
end
if full_table and group_col then
t[#t + 1] = '| class="' .. mw.text.trim(bg_class) .. '" style="' .. ii_fw .. bg_col .. '" |' .. group_txt .. '\n'
end
local team_string
if t_status.position == 'before' then
team_string = status_string .. team_name .. note_string
else
team_string = team_name .. note_string .. status_string
end
-- Название команды — заголовок строки, поэтому ячейка строится через «!».
t[#t + 1] = '! scope="row" class="st-team' .. bg_class .. '" style="' .. ii_fw .. bg_col .. '"| ' .. team_string .. '\n'
end
-- Добавляет в t ячейку столбца результатов для группы строк, начинающейся с позиции ii
-- (см. result_groups); на остальных строках группы ничего не делает. res_col —
-- {groups, layout}: группы строк и места разрывов текста в них (ppp.result_layout).
-- Ячейка выводится, только если столбец показывается (results_defined). Возвращает notes_exist.
local function append_results_cell(t, frame, Args, respre, rand_val, res_col, get_result_text, note_id_list, ii, ii_show, notes_exist, results_defined)
local group = res_col.groups[ii]
if not group then
return notes_exist
end
local N_rows_res = group.n
local result_local = group.result
-- Полужирным — если команда showteam (ii_show) входит в группу.
local ii_fw
if ii_show and (ii_show >= ii) and (ii_show <= (ii + N_rows_res - 1)) then
ii_fw = 'font-weight:bold;'
else
ii_fw = 'font-weight:normal;'
end
local bg_col = resolve_bg_col(Args, respre, result_local ~= '' and result_local or nil)
-- Класс заливки считается здесь, а не берётся у строки: ячейка объединяет несколько
-- строк с одним результатом, и цвет у неё свой — тот, что у первой строки группы.
local bg_class = filled_class(Args, respre, result_local ~= '' and result_local or nil)
local note_res_string = ''
local note_ref, note_text, note_id
-- Примечание к результату (note_res_<код>) обрабатывается, только если столбец выводится:
-- шаблон сноски регистрирует примечание уже при вызове expandTemplate,
-- даже если ячейка не выводится, и в колонтитуле появилась бы пустая секция примечаний.
if results_defined and Args['note_res_' .. result_local] then
notes_exist = true
local note_res_local = Args['note_res_' .. result_local]
-- Значение — список через запятую, если каждый его элемент — код другого результата
-- со своим note_res_; иначе оно целиком считается текстом примечания.
local multiref = 1
local note_res_local_table = mw.text.split(note_res_local, '%s*,%s*')
if (#note_res_local_table > 1) then
for _, note_res_loc in ipairs(note_res_local_table) do
multiref = multiref * (Args['note_res_' .. note_res_loc] and 1 or 0)
end
else
multiref = 0
end
if multiref < 1 then
note_res_local_table = {note_res_local}
end
for _, note_res_item in ipairs(note_res_local_table) do
if not Args['note_res_' .. note_res_item] then
-- Собственный текст примечания.
note_ref = respre .. 'res_' .. result_local
note_id = '"table_note_res_' .. result_local .. rand_val .. '"'
note_text = note_res_item
else
-- Отсылка к примечанию другого результата: берётся его текст.
note_ref = respre .. 'res_' .. note_res_item
note_id = '"table_note_res_' .. note_res_item .. rand_val .. '"'
note_text = Args['note_res_' .. note_res_item]
end
-- Первое упоминание выводит сноску с текстом (efn), повторные — ссылку на неё (ref).
if not note_id_list[note_ref] then
note_id_list[note_ref] = note_id
note_res_string = note_res_string .. frame:expandTemplate{title = 'efn',
args = {group = 'lower-alpha', name = note_id, note_text}
}
else
note_res_string = note_res_string .. frame:extensionTag{name = 'ref',
args = {group = 'lower-alpha', name = note_id}
}
end
end
end
-- Текст из шаблона fbmulticomp (опознаётся по имени в разметке) занимает ячейку без отступов,
-- и места переноса в нём модуль не расставляет; fbmulticompefn означает, что внутри есть сноски.
local text_result = get_result_text(result_local) or ''
if text_result:match('fbmulticomp') then
ii_fw = 'padding:0;' .. ii_fw
if text_result:match('fbmulticompefn') then
notes_exist = true
end
else
-- Места переноса расставляются по правилам столбца — см. ppp.wrap_result_text.
text_result = p_sub.wrap_result_text(text_result, res_col.layout[ii])
end
local text_field_result = '| class="' .. mw.text.trim(bg_class) .. '" style="' .. ii_fw .. bg_col
.. '" rowspan="' .. N_rows_res .. '" |' .. text_result .. note_res_string .. '\n'
if results_defined then
t[#t + 1] = text_field_result
end
return notes_exist
end
-- Дописывает в строку команды ii клетки шахматки: разделительную ячейку и результаты
-- (p_matches.row). Перед этим p_matches.match_notes подставляет в значения match_ХХХ_УУУ
-- знаки сносок к результатам. Возвращает t и notes_exist.
local function append_match_row(t, frame, Args, rand_val, note_id_list, p_matches, top_pos, N_teams, team_list, ii, ii_start, ii_end, legs, ii_show, notes_exist)
t[#t + 1] = '| class="st-spacer"| \n'
if p_matches.match_notes(frame, Args, rand_val, note_id_list, top_pos, N_teams, team_list, ii, ii_start, ii_end, legs) then
notes_exist = true
end
t = p_matches.row(t, Args, N_teams, top_pos, team_list, ii, ii_show, legs)
return t, notes_exist
end
-- Строит нижний колонтитул: дату обновления, источник, правила классификации, пояснения к заливке клеток шахматки,
-- легенду статусов и список примечаний. Возвращает разметку блока reflist, а при notes_exist —
-- ещё и шаблон «Комментарии» со сносками группы lower-alpha.
local function build_footer(frame, Args, t_status, letter_wrapper, full_table, show_class_rules, hide_footer, notes_exist, title_source, p_matches)
local t_footer = {}
-- Без source ставится шаблон «нет источника»,
-- если источник не дописан к заголовку (title_source).
local update = Args['update'] or '(дата неизвестна)'
local start_date = Args['start_date'] or '(дата неизвестна)'
local source = Args['source'] or (title_source == true and '') or frame:expandTemplate{
title = 'нет источника',
args = {reason = 'Источник не задан', date = os.date('%B %Y')}
}
local class_rules = Args['class_rules'] or nil
-- Дата обновления. При update=complete или hide_footer строки нет,
-- при update=future выводится дата начала (start_date).
local matches_text = Args['matches_text'] or 'матчей'
local update_mode = string.lower(update)
if update_mode ~= 'complete' and not hide_footer then
if update == '' then
table.insert(t_footer, 'Обновлено для ' .. matches_text .. ', сыгранных на. ')
elseif update_mode == 'future' then
table.insert(t_footer, 'Первые матчи пройдут ' .. start_date .. '. ')
else
table.insert(t_footer, 'Обновлено для ' .. matches_text .. ', сыгранных на ' .. update .. '. ')
end
end
-- stack_footer=yes ставит источник с новой строки после даты,
-- а буквы легенды статусов — каждую на свою строку.
local footer_break = flag(Args['stack_footer'], 'no')
local stack_string = '<br>'
if footer_break and string.lower(update) ~= 'complete' and not hide_footer then
table.insert(t_footer, stack_string)
end
if source ~= '' and not hide_footer then
table.insert(t_footer, 'Источник: ' .. source)
end
if class_rules and full_table and show_class_rules and not hide_footer then
if (#t_footer > 0) then
table.insert(t_footer, '<br>')
end
table.insert(t_footer, 'Правила классификации: ' .. class_rules)
end
-- Пояснения к заливке клеток шахматки внутри таблицы (show_matches=yes) —
-- те же, что под самостоятельной шахматкой: см. p_matches.footer_legend.
if p_matches and not hide_footer then
for _, sentence in ipairs(p_matches.footer_legend(Args)) do
if #t_footer > 0 then
table.insert(t_footer, '<br>')
end
table.insert(t_footer, sentence)
end
end
-- Легенда статусов: буквы, отмеченные в t_status.called, в порядке t_status.letters.
local status_exist = false
local status_string = ''
local curr_letter
-- Строка перебирается побайтово, в расчёте на латиницу:
-- буква не латиницей, заданная в status_order, в легенду не попадёт.
for jjj = 1, #t_status.letters do
curr_letter = string.upper(t_status.letters:sub(jjj, jjj))
if t_status.called[curr_letter] then
if (footer_break and status_exist) then
status_string = status_string .. stack_string
end
if t_status.position == 'before' then
status_string = status_string .. '<span class="st-status">' .. string.lower(letter_wrapper(curr_letter)) .. ' –</span> ' .. t_status.code[curr_letter] .. '; '
else
status_string = status_string .. '<span class="st-status">(' .. letter_wrapper(curr_letter) .. ')</span> ' .. t_status.code[curr_letter] .. '; '
end
status_exist = true
end
end
-- Легенда выводится без завершающего «; ».
if status_exist and not hide_footer then
if (#t_footer > 0) then
table.insert(t_footer, '<br>')
end
status_string = ustring.sub(status_string, 1, ustring.len(status_string) - 2)
table.insert(t_footer, status_string)
end
local footer_text
if notes_exist then
if (#t_footer > 0) then
table.insert(t_footer, '<br>')
end
table.insert(t_footer, 'Примечания:')
footer_text = '<div class="reflist">' .. table.concat(t_footer) .. '</div>'
footer_text = footer_text .. frame:expandTemplate{title = 'Комментарии',
args = {group = 'lower-alpha'}
}
else
footer_text = '<div class="reflist">' .. table.concat(t_footer) .. '</div>'
end
return footer_text
end
-- Дописывает в t замечания предпросмотра и отслеживающие категории: подсказку о ширине столбцов,
-- итоги проверки параметров, команды без показателей, ошибки автосборки, сверку итогов и
-- сверку сокращений с реестром. При notracking=yes после подсказки о ширине
-- ставится только категория «без отслеживания», и остальные проверки не выполняются.
-- На страницах, исключённых ppp.tracking_excluded, категорий нет; замечаний проверки
-- параметров там нет и в предпросмотре — они подставляются на место своих категорий.
-- match_legs — число кругов шахматки внутри таблицы (для подсказок о дерби) или nil.
local function append_tracking_categories(t, frame, Args, currentTitle, team_list, ii_start, ii_end, in_preview, auto_errors, match_legs)
-- Подсказка о ширине столбцов названий и очков, подобранной модулем.
-- Нужна, когда турнир разбит на группы: у каждой таблицы ширина своя,
-- и рядом они выглядят несогласованно. Увидев значения всех групп,
-- редактор задаёт наибольшее из них в teamwidth и pointswidth каждой — тогда ширина становится единой.
-- Не выводится, когда ширина задана вручную, и когда подобранное значение не превышает
-- нижней границы (10em для названий, 2,5em для очков).
if in_preview() then
local wide = {}
if not Args['teamwidth'] then
local measured = p_sub.measure_team_width(Args)
if (tonumber(measured:match('^([%d%.]+)')) or 0) > 10 then
wide[#wide + 1] = 'команд ' .. measured .. ' (|teamwidth=)'
end
end
-- Столбец очков шире, когда очки трёхзначные или к ним приписаны сноски.
-- Из-за сносок соседние таблицы расходятся по ширине сильнее всего:
-- в одной группе команды разводили личными встречами, в другой нет.
if not Args['pointswidth'] then
local measured = p_sub.measure_points_width(Args)
if (tonumber(measured:match('^([%d%.]+)')) or 0) > 2.5 then
wide[#wide + 1] = 'очков ' .. measured .. ' (|pointswidth=)'
end
end
if #wide > 0 then
t[#t + 1] = p_sub.preview_note('info', 'Столбцы шире обычного: ' .. table.concat(wide, ', ')
.. '. Чтобы таблицы на странице совпали по ширине, задайте эти параметры '
.. 'наибольшими из значений во всех таблицах.')
end
end
local exclude_tracking_categories = p_sub.tracking_excluded(currentTitle)
if flag(Args['notracking'], 'no') then
if not exclude_tracking_categories then
table.insert(t, '[[Категория:Страницы, использующие спортивные таблицы без отслеживания]]')
end
return
end
local getTracking = require('Модуль:Sports table/argcheck').check
local warning_categories, tracking_categories = getTracking(Args, frame:getParent().args)
if #warning_categories > 0 then
if in_preview() then
for k = 1, #warning_categories do
warning_categories[k] = p_sub.preview_note('error', p_sub.warning_text(warning_categories[k]))
end
end
end
if not exclude_tracking_categories then
t[#t + 1] = p_sub.direct_invoke_category(frame)
for k = 1, #warning_categories do
t[#t + 1] = warning_categories[k]
end
for k = 1, #tracking_categories do
t[#t + 1] = tracking_categories[k]
end
end
-- Команда без единого заполненного показателя. Незаполненный показатель модуль считает нулём,
-- и такая строка неотличима от команды без побед и голов. Обычная причина —
-- опечатка в коде команды: показатели записаны под одним кодом, а сама она — под другим.
-- При автосборке проверка не нужна: показатели считает модуль.
if not flag(Args['auto_generate_standings'], 'no') then
local blank = {}
for ii = ii_start, ii_end do
local code = team_list[ii]
if code and code ~= p_sub.total_row_code and not p_sub.has_stats(Args, code) then
blank[#blank + 1] = code
end
end
if #blank > 0 then
if in_preview() then
t[#t + 1] = p_sub.preview_note('warning', 'не заполнено ни одного показателя: '
.. table.concat(blank, ', ') .. '. Пустые показатели считаются нулями — '
.. 'проверьте, не разошёлся ли код команды с кодом в её показателях')
end
if not exclude_tracking_categories then
t[#t + 1] = '[[Категория:Страницы, использующие спортивные таблицы с командой без показателей]]'
end
end
end
-- Ошибки автосборки, в том числе стиль без её поддержки. Выводятся красным, в отличие
-- от зелёной сверки итогов: это не расхождение источников, а неверно заполненная
-- шахматка — опечатка в коде команды, нечитаемый счёт, повтор пары, нехватка матчей.
-- Категория ставится и на сохранённой странице: при ошибке автосборка строит таблицу
-- по тем матчам, которые смогла прочитать, и по виду страницы этого не заметно.
if auto_errors and #auto_errors > 0 then
if in_preview() then
for k = 1, #auto_errors do
t[#t + 1] = p_sub.preview_note('error', auto_errors[k])
end
end
if not exclude_tracking_categories then
t[#t + 1] = '[[Категория:Страницы, использующие спортивные таблицы с ошибками автосборки]]'
end
end
-- Сверка итогов ([[Модуль:Sports table/totalscheck]]) не выполняется при showteam (team)
-- и при автосборке. При автосборке показатели вычислены из тех же матчей, с которыми их
-- предлагается сверять, и обе проверки становятся тождествами. Осмысленными остаются
-- проверки самой автосборки — строение шахматки и её комплектность — и сверка счёта
-- с карточками матча, которая сравнивает два независимо заполненных источника.
if ((Args['showteam'] or Args['team']) == nil) and not flag(Args['auto_generate_standings'], 'no') then
local totalscheck = require('Модуль:Sports table/totalscheck')
local total_warnings = totalscheck.check(Args, team_list, ii_start, ii_end, yesno)
if #total_warnings > 0 then
if in_preview() then
for k = 1, #total_warnings do
t[#t + 1] = p_sub.preview_note('warning', total_warnings[k])
end
end
end
-- Показатели команд против результатов матчей match_ХХХ_УУУ, если они заданы.
local match_warnings, confirmed = totalscheck.check_matches(Args, team_list, ii_start, ii_end)
if #match_warnings > 0 then
if in_preview() then
for k = 1, #match_warnings do
t[#t + 1] = p_sub.preview_note('warning', match_warnings[k])
end
end
end
-- Категория — только за расхождения у команд с полностью заполненной шахматкой
if confirmed > 0 and not exclude_tracking_categories then
t[#t + 1] = '[[Категория:Страницы, где показатели команд расходятся с результатами матчей]]'
end
end
-- Сверка сокращений команд с реестром — ppp.check_codes. Выполняется только там, где
-- лежат данные (ppp.data_page): на странице шаблона и его документации либо в статье
-- с прямым вызовом модуля. Статьи, подключающие шаблон, реестр не загружают вовсе:
-- сокращение — решение уровня шаблона, и исправляют его там же.
local data_page = p_sub.data_page(frame)
local registry = data_page and p_sub.code_registry_data()
if registry and registry.teams then
local mismatches, missing, undecided = p_sub.check_codes(Args, registry.teams, registry.pending)
if in_preview() then
for _, m in ipairs(mismatches) do
t[#t + 1] = p_sub.preview_note('warning', 'сокращение ' .. m.shown .. ' — в реестре «'
.. m.team .. '» записан как ' .. m.want)
end
if #undecided > 0 then
t[#t + 1] = p_sub.preview_note('info', 'сокращение ещё не выбрано в реестре: '
.. table.concat(undecided, ', ') .. ' — см. раздел pending в [[' .. p_sub.registry_page .. ']]')
end
if #missing > 0 then
t[#t + 1] = p_sub.preview_note('info', 'нет в реестре сокращений: '
.. table.concat(missing, ', ') .. '. Пополнить реестр можно вызовом '
.. '{{#invoke:Sports group|codes|…}} — см. [[Модуль:Sports table/doc#Реестр сокращений]]')
end
end
if #mismatches > 0 and not exclude_tracking_categories then
t[#t + 1] = '[[Категория:Страницы, использующие спортивные таблицы с сокращениями вне реестра]]'
end
-- Подсказки о статьях про дерби для шахматки внутри таблицы — ppp.derby_hints.
-- Самостоятельную шахматку и шахматку под таблицей проверяет [[Модуль:Sports results]].
if match_legs and in_preview() then
for _, hint in ipairs(p_sub.derby_hints(Args, team_list, ii_start, ii_end, match_legs, registry.derbies)) do
t[#t + 1] = p_sub.preview_note('info', 'дерби: ' .. hint)
end
end
end
end
-- Экспортируемые функции ----------------------------------------------------------------
-- Строит турнирную таблицу по готовому набору аргументов и возвращает её разметку вместе
-- с колонтитулом, замечаниями предпросмотра и категориями.
-- Отделена от p.main, чтобы её мог вызвать другой модуль: [[Модуль:Sports group]] собирает
-- таблицу и отчёты о матчах одним #invoke и передаёт сюда уже отобранные аргументы. Args
-- изменяется на месте: автосборка, team_order, строка итогов и источник заголовка записывают в него свои поля.
-- frame нужен для разворачивания шаблонов и для обращения к родительскому вызову.
function p._main(frame, Args)
local ii_start, ii_end
local notes_exist = false
local t = {}
local t_return
local team_list = {}
-- Якорь таблицы при section: «sports-table-<section>», пробелы заменены на «_».
local table_anchor = ustring.gsub(Args['section'] and 'sports-table-' .. Args['section'] or '', ' ', '_')
-- Обёртка над ppp.in_preview ради краткости:
-- вызовов много, в том числев append_tracking_categories, куда она передаётся.
local function in_preview()
return p_sub.in_preview(frame)
end
local currentTitle = mw.title.getCurrentTitle()
-- Вызов для другого раздела страницы ничего не выводит — см. ppp.skip_section.
if p_sub.skip_section(frame) then
return ''
end
-- При show_matches=only вместо турнирной таблицы выводится шахматка:
-- вызов целиком передаётся p_results._main с теми же аргументами
-- (team_order, name_ХХХ, match_ХХХ_УУУ и т. д.).
if string.lower(Args['show_matches'] or '') == 'only' then
-- Параметры проверяются здесь, набором турнирной таблицы; при notracking=yes — нет.
-- Шахматка проверяет их сама только при вызове без style — так она отличает
-- самостоятельный вызов от делегированного.
-- Без этой проверки путь show_matches=only со style оставался бы непроверенным,
-- а именно им шахматку выводят в шаблонах групповых этапов.
local p_results = require('Модуль:Sports results')
local checks = ''
if not flag(Args['notracking'], 'no') then
local warn, track = require('Модуль:Sports table/argcheck').check(Args, frame:getParent().args)
local exclude = p_sub.tracking_excluded(currentTitle)
if p_sub.in_preview(frame) then
for k = 1, #warn do
checks = checks .. p_sub.preview_note('error', p_sub.warning_text(warn[k]))
end
end
if not exclude then
checks = checks .. table.concat(warn) .. table.concat(track)
end
end
-- Аргументы передаются уже прочитанные: перечитывать их из frame значило бы при
-- вызове из Sports group развернуть и данные матчей, которые здесь не нужны.
return p_results._main(frame, Args) .. checks
end
-- Дробные очки и поправки можно записывать по-русски — «0,5», «−3»: они приводятся к виду,
-- который читают автосборка, стиль и сверки (см. ppp.read_decimals).
p_sub.read_decimals(Args)
-- Нужна только цель навбара — см. resolve_baselink.
local _, template_name = resolve_baselink(frame, currentTitle, Args, table_anchor)
-- Позиции таблицы: от highest_pos (по умолчанию 1) до lowest_pos; lowest_pos меньше
-- highest_pos (по умолчанию 0) снимает ограничение. N_teams начинается с позиции перед
-- первой: teamN с номерами меньше highest_pos не читаются.
local top_pos = tonumber(Args['highest_pos']) or 1
local bottom_pos = tonumber(Args['lowest_pos']) or 0
local N_teams = top_pos - 1
-- Подмодуль стиля. Имя приходит из параметра, поэтому загрузка защищена: при опечатке
-- или удалённом стиле такого подмодуля нет, и обычный require уронил бы весь модуль.
-- Вместо этого берётся стиль по умолчанию: таблица строится, пусть и не тем стилем,
-- который задавал редактор. Нераспознанный style отмечает отдельной категорией.
local style_def = Args['style'] or 'WDL'
local ok, p_style = pcall(require, 'Модуль:Sports table/' .. style_def)
if not ok then
p_style = require('Модуль:Sports table/WDL')
end
-- Случайное число отличает идентификаторы сносок этой таблицы от сносок других таблиц
-- на странице. Генератор засевается один раз при загрузке общего подмодуля (p_sub).
local rand_val = math.random()
local full_table, hide_results, hide_footer, show_class_rules = resolve_table_mode(Args)
-- results_defined — выводится ли столбец результатов; решается ниже, после выбора строк.
local results_defined = false
local respre, results_header_txt
respre, results_header_txt, notes_exist = build_results_header(Args, full_table, frame, notes_exist)
-- Буквы и тексты статусов — от подмодуля стиля (см. ppp.status).
local t_status = p_style.status(Args)
-- Автосборка ([[Модуль:Sports table/auto]]): показатели команд вычисляются по
-- результатам матчей и записываются в Args, после чего таблица строится обычным порядком.
-- Вызывается до apply_team_order: автосборка сама читает teamN и team_order, убирает их
-- и записывает итоговый порядок в teamN.
-- Признаки берутся у самого стиля, а не перечисляются здесь списком: так новый стиль
-- не требует правок в этом модуле. auto_supported объявляют стили, таблицу которых можно собрать
-- из результатов матчей. write_ha объявляют стили, читающие раздельные домашние и выездные показатели:
-- общих полей они не читают вовсе, и проверка параметров отметила бы их как неизвестные. write_ot объявляют стили
-- со столбцами овертайма и серии: исходы для них автосборка берёт из result_ХХХ_УУУ.
local auto_errors
if flag(Args['auto_generate_standings'], 'no') then
if p_style.auto_supported then
auto_errors = require('Модуль:Sports table/auto').generate(Args, top_pos,
p_style.write_ha, p_style.write_ot)
else
auto_errors = {'стиль ' .. style_def .. ' не поддерживает |auto_generate_standings='}
end
end
apply_team_order(Args)
-- В предпросмотре строка итогов включается сама: суммы по столбцам видны рядом
-- с предупреждениями сверки итогов. В сохранённой статье она выводится только
-- при show_totals=yes.
-- Не включается она, когда show_totals задан явно (редактор уже решил), когда
-- задан no_totals_check=yes (сверка отключена вручную) и когда таблица собрана
-- из команд разных групп по одной — рейтинг вида «команды, занявшие третьи места»:
-- там сумма побед не равна сумме поражений, и итоги были бы заведомо неверными.
-- Последнее условие — то же, по которому пропускается сверка сумм, и берётся из того же
-- места (totalscheck.is_cross_group), чтобы правила не разошлись.
local totals_set = Args['show_totals'] ~= nil and Args['show_totals'] ~= ''
if in_preview() and not totals_set and not flag(Args['no_totals_check'], 'no') then
local groups = {}
local ii = 1
while Args['team' .. ii] do
groups[#groups + 1] = Args['group_' .. Args['team' .. ii]]
ii = ii + 1
end
if not require('Модуль:Sports table/totalscheck').is_cross_group(groups) then
Args['show_totals'] = 'yes'
end
end
local total_row_name, show_totals
N_teams, total_row_name, show_totals = build_team_list(Args, team_list, top_pos, bottom_pos, N_teams)
local position_col = flag(Args['show_positions'], 'yes')
local group_col = flag(Args['show_groups'], 'no')
-- show_matches=below и below_levelN: шахматка выводится не внутри таблицы, а отдельным
-- блоком под ней, с собственным заголовком раздела. Уровень заголовка задаётся суффиксом:
-- below_level3 даёт «===»; без суффикса — второй уровень, как у разделов статьи.
-- Расписание (matches_format=schedule, series, series_split) в строки таблицы не встроить:
-- у него свои столбцы — туры, а не соперники. Поэтому при нём show_matches=yes тоже выводит
-- его под таблицей.
local below_level = nil
do
local sm = string.lower(Args['show_matches'] or '')
local fmt = mw.ustring.lower(mw.text.trim(Args['matches_format'] or ''))
local schedule = fmt == 'schedule' or fmt == 'series' or fmt == 'series_split'
if sm == 'below' or (schedule and flag(Args['show_matches'], 'no')) then
below_level = 2
else
local n = sm:match('^below_level(%d)$')
if n then
below_level = tonumber(n)
end
end
end
-- Шахматка внутри таблицы (show_matches=yes); при below она выводится отдельно.
local match_table = not below_level and flag(Args['show_matches'], 'no')
local p_matches = match_table and require('Модуль:Sports results')
-- Число кругов нужно и шапке, и строкам шахматки.
local legs = tonumber(Args['legs']) or 1
-- Подпись столбца мест — postitle; без столбца её нет.
local pos_label = Args['postitle'] or '<abbr title="Позиция">Поз</abbr>'
if position_col == false then
pos_label = nil
end
local show_status = flag(Args['show_status'], 'yes')
local VTE_text = build_vte_text(template_name, Args)
local title_source = apply_title_source(Args)
if table_anchor ~= '' then
table.insert(t, '<span class="anchor" id="' .. table_anchor .. '"></span>\n')
end
-- Шапка таблицы.
local N_cols
t, N_cols = build_header(p_style, t, Args, pos_label, group_col, VTE_text, full_table, results_header_txt, match_table, p_matches, N_teams, top_pos, team_list, legs)
-- Выводимые позиции — см. compute_display_range.
local ii_show = team_list[Args['showteam'] or Args['team']]
local n_to_show = tonumber(Args['show_limit']) or N_teams
ii_start, ii_end = compute_display_range(top_pos, N_teams, ii_show, n_to_show)
-- Столбец результатов. result_by_pos — код результата (resultN) каждой выводимой позиции.
local result_by_pos = {}
for ii = ii_start, ii_end do
result_by_pos[ii] = Args[respre .. 'result' .. ii]
end
-- Текст результата (text_<код>) по коду, с запоминанием; false в кэше — «текста нет».
local text_by_result = {}
local function get_result_text(result_val)
local cached = text_by_result[result_val]
if cached == nil then
cached = Args[respre .. 'text_' .. result_val] or false
text_by_result[result_val] = cached
end
if cached == false then
return nil
end
return cached
end
-- Столбец выводится, если хоть у одной выводимой позиции есть текст результата
-- и он не скрыт параметром hide_results.
if not hide_results then
for ii = ii_start, ii_end do
if result_by_pos[ii] and get_result_text(result_by_pos[ii]) then
results_defined = true
end
end
end
-- Группы строк столбца результатов и раскладка их текста. Раскладка считается по всем
-- ячейкам сразу: ширину столбца задают все они вместе с заголовком.
local res_col = {groups = result_groups(Args, result_by_pos, ii_start, ii_end), layout = {}}
if full_table and results_defined then
local cells, starts = {}, {}
for ii = ii_start, ii_end do
local group = res_col.groups[ii]
if group then
cells[#cells + 1] = {text = get_result_text(group.result) or '', rows = group.n,
tail = Args['note_res_' .. group.result] and 2 or 0}
starts[#starts + 1] = ii
end
end
local layout = p_sub.result_layout(cells, results_header_txt:match('|([^\n]*)') or '')
for k, ii in ipairs(starts) do
res_col.layout[ii] = layout[k]
end
end
-- Без столбца результатов из готовой шапки удаляется его заголовок: разметка
-- сворачивается в одну строку, и заголовок вырезается из неё. Заголовок ищется как простой
-- текст, а не как шаблон Lua: скобки, «-» и «%» в подписи или в сноске к ней не мешают.
if full_table and not results_defined then
local t_str = table.concat(t)
local cut_from, cut_to = t_str:find(results_header_txt, 1, true)
if cut_from then
t_str = t_str:sub(1, cut_from - 1) .. t_str:sub(cut_to + 1)
end
N_cols = N_cols - 1
t = {}
table.insert(t, t_str)
end
-- Строки таблицы.
local team_name, team_code_ii, pos_num, group_txt
local note_string, note_local
local note_id_list = {}
local hth_id_list = {}
-- Буквы статуса выводятся русскими (rus_letters), если не задано
-- keep_english_status_labels=yes.
local letter_wrapper
if flag(Args["keep_english_status_labels"], 'no') then
letter_wrapper = function(x)
return x
end
else
letter_wrapper = function(x)
return rus_letters[x] or x
end
end
for ii = ii_start, ii_end do
team_code_ii = team_list[ii]
-- pos_ХХХ заменяет выводимый номер места, name_ХХХ — название (без него выводится код).
pos_num = Args['pos_' .. team_code_ii] or ii
group_txt = Args['group_' .. team_code_ii] or ' '
team_name = Args['name_' .. team_code_ii] or team_code_ii
note_local = Args['note_' .. team_code_ii] or nil
-- Код результата позиции: по нему берутся заливка ячеек строки (встроенным стилем
-- или классом, см. resolve_bg_col и filled_class) и текст столбца результатов.
local result_local = result_by_pos[ii]
local bg_col = resolve_bg_col(Args, respre, result_local)
local bg_class = filled_class(Args, respre, result_local)
-- Полужирным выделяются команда showteam и строка итогов.
local ii_fw = ii == ii_show and 'font-weight:bold;' or 'font-weight:normal;'
if show_totals and team_code_ii == total_row_name then
ii_fw = 'font-weight:bold;'
end
if note_local and full_table then
notes_exist = true
-- note_ХХХ — собственный текст сноски либо код другой команды, чьё примечание
-- переиспользуется: при note_ААА=Текст и note_БББ=ААА у обеих одна сноска.
-- Выведена ли та сноска в показанной части таблицы, решает ppp.build_note.
local note_local_ref_text = Args['note_' .. note_local]
local is_own = not note_local_ref_text
if is_own then
note_string = p_sub.build_note(frame, rand_val, note_id_list, ii_start, ii_end, team_code_ii, note_local, true)
else
note_string = p_sub.build_note(frame, rand_val, note_id_list, ii_start, ii_end, team_code_ii, nil, false, note_local, team_list[note_local], note_local_ref_text)
end
else
note_string = ''
end
-- Буквы статуса (status_ХХХ); show_status=no их скрывает.
local status_local = show_status and Args[respre .. 'status_' .. team_code_ii] or nil
local status_string = build_status_string(t_status, status_local, letter_wrapper)
append_row_lead_cells(t, show_totals, team_code_ii, total_row_name, position_col, ii_fw, bg_col, bg_class, pos_num, full_table, group_col, group_txt, t_status, status_string, team_name, note_string)
-- Ячейки показателей строит подмодуль стиля.
t_return = p_style.row(frame, t, Args, notes_exist, hth_id_list, full_table, rand_val, team_list, team_code_ii, ii_start, ii_end, ii_fw, bg_col, bg_class, ii)
t = t_return.t
notes_exist = t_return.notes_exist
hth_id_list = t_return.hth_id_list
if full_table then
notes_exist = append_results_cell(t, frame, Args, respre, rand_val, res_col, get_result_text, note_id_list, ii, ii_show, notes_exist, results_defined)
end
-- Клетки шахматки в той же строке (show_matches=yes).
if match_table then
t, notes_exist = append_match_row(t, frame, Args, rand_val, note_id_list, p_matches, top_pos, N_teams, team_list, ii, ii_start, ii_end, legs, ii_show, notes_exist)
end
-- splitN — разделительная линия после позиции N, кроме последней выводимой.
-- Высоту и заливку линии, в том числе в тёмной теме, задаёт класс st-split.
if Args['split' .. ii] and (ii < ii_end) then
-- Ширина линии зависит от N_cols: нужно 2·N_cols знаков «|».
t[#t + 1] = '|- class="st-split"\n'
t[#t + 1] = string.rep('|', 2 * N_cols) .. '\n'
end
end
table.insert(t, '|}')
-- Нижний колонтитул.
local t_footer = build_footer(frame, Args, t_status, letter_wrapper, full_table, show_class_rules, hide_footer, notes_exist, title_source,
match_table and p_matches or nil)
table.insert(t, t_footer)
-- Ссылки со счёта на якоря отчётов о матчах ведутся туда, где эти отчёты лежат;
-- решает ppp.report_link_resolver, по каждому якорю отдельно.
p_sub.resolve_report_links(frame, Args, t)
append_tracking_categories(t, frame, Args, currentTitle, team_list, ii_start, ii_end, in_preview, auto_errors,
match_table and legs or nil)
-- Шахматка под таблицей (show_matches=below): p_results._main, как и при
-- show_matches=only, но с переопределениями. matches_title, matches_team_header
-- и matches_source (по умолчанию source) заменяют заголовок, подпись угловой ячейки
-- и источник; незаданные matches_title и matches_team_header ключа в override не дают,
-- и шахматка берёт title и team_header турнирной таблицы.
-- matches_note_ХХХ передаётся шахматке как её note_ХХХ; значение вида note_ХХХ заменяется
-- текстом этого примечания таблицы. Без matches_team_notes=yes остальным выводимым
-- командам передаётся пустой note_ХХХ: пустое примечание шахматка не выводит, и примечания
-- таблицы в неё не переходят.
if below_level then
local override = {
['show_matches'] = 'only',
['title'] = Args['matches_title'],
['team_header'] = Args['matches_team_header'],
['source'] = Args['matches_source'] or Args['source'],
}
local pass_notes = flag(Args['matches_team_notes'], 'no')
for ii = ii_start, ii_end do
local code = team_list[ii]
if code then
local own = Args['matches_note_' .. code]
if own then
override['note_' .. code] = own:match('^note_') and Args[own] or own
elseif not pass_notes then
override['note_' .. code] = ''
end
end
end
local header = Args['below_results_header'] or 'Результаты'
local eq = string.rep('=', below_level)
t[#t + 1] = '\n' .. eq .. ' ' .. header .. ' ' .. eq .. '\n'
t[#t + 1] = require('Модуль:Sports results')._main(frame, Args, override)
end
-- Обёртка горизонтальной прокрутки. Столбцов у таблицы до одиннадцати, и на узком
-- экране она шире его: без обёртки горизонтально едет вся страница вместе с заголовком
-- и текстом статьи, а с обёрткой — только сама таблица.
-- Устроена так же, как у шахматки: внешний блок обрезает, внутренний прокручивает.
-- Класс noresize нужен, чтобы приложение не пыталось вписать её в экран собственными средствами.
-- Перевод строки после открывающих блоков нужен: разметка «{|», которой начинается
-- таблица, разбирается, только когда стоит в начале строки. Перед закрывающими тегами
-- он тоже нужен — иначе «|}» окажется не в начале своей строки.
return p_sub.styles()
.. '<div class="st-scroll-outer"><div class="noresize st-scroll-inner">\n'
.. table.concat(t)
.. '\n</div></div>'
end
-- Точки входа для #invoke ---------------------------------------------------------------
-- Читает аргументы вызова — параметры шаблона имеют преимущество перед параметрами самого
-- #invoke (parentFirst) — и передаёт их p._main.
function p.main(frame)
local getArgs = require('Модуль:Arguments').getArgs
return p._main(frame, getArgs(frame, {parentFirst = true}))
end
-- Сборщик сокращений команд для реестра — s.codes в [[Модуль:Sports table/source]].
-- Та же точка входа есть у Sports results и Sports group.
function p.codes(frame)
return require('Модуль:Sports table/source').codes(frame)
end
return p