Документация

Этот модуль предназначен для построения таблиц результатов матчей различных соревнований и реализует работу шаблона {{таблица результатов матчей}}.


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

Основная команда {{#invoke:Sports results|main}} вызывает модуль и устанавливает базовую структуру. Затем идёт перечисление команд в том порядке, в котором это необходимо через параметры |team1 =, |team2 =, |teamN =. Таблица строится таким образом, что будущие игры могут быть перечислены с указанием их даты или же могут быть оставлены пустыми. С помощью параметра |matches_style = можно раскрасить ячейки в зависимости от результата матча: зелёный (#bbf3bb) — победа хозяев; жёлтый (#ffffbb) — ничья; розовый (#ffbbbb) — победа гостей. С помощью параметра |showteam = можно выделить результаты конкретной команды. С помощью параметра |source = можно указать источник данных, а также дату обновления с помощью параметра |update =. Для завершившихся соревнований данный параметр необходимо использовать как |update = complete, для предстоящих — |update = future или через параметр |start_date =. Примечание может быть добавлено к результату с помощью параметра |match_XXX_YYY_note =.

-- Модуль для построения таблиц результатов в спорте

require('strict')

local p = {}

-- Main function
function p.main(frame)
-- Получаем аргументы, удаляя пустые значения
	local getArgs = require('Модуль:Arguments').getArgs
	local Args = getArgs(frame, {parentFirst = true})

-- Exit early if we are using section transclusion for a different section
	local tsection = frame:getParent().args['transcludesection'] or frame:getParent().args['section'] or ''
	local bsection = frame.args['section'] or ''
	if ( tsection ~= '' and bsection ~= '') then
		if ( tsection ~= bsection) then
			return ''
		end
	end

-- Declare locals
	local t = {}
	local t_footer = {}
	local t_return = {}
	local team_list = {}
	local notes_exist = false
	local ii, ii_fw, bg_col, team_name, team_code_ii, ii_start, ii_end
-- Optional custom team header
	local team_header = Args['team_header'] or 'Дома \\ На выезде'
-- Number of legs
	local legs = tonumber(Args['legs']) or 1
	local multirowlegs = (Args['multirowlegs'] or 'no') ~= 'no'

-- Получаем пользовательскую начальную точку для таблицы (большинство из них по умолчанию начинаются с 1)
	local top_pos = tonumber(Args['highest_pos']) or 1
-- Получаем пользовательскую конечную точку для таблицы (неограниченную, если bottom_pos равно < top_pos)
	local bottom_pos = tonumber(Args['lowest_pos']) or 0
	local N_teams = top_pos - 1 -- Значение по умолчанию равно 0 при запуске, но для пропуска определенных записей требуется большее число

-- Load some other modules
	local p_sub = require('Модуль:Sports table/sub')

-- Альтернативный синтаксис для списка команд
	if Args['team_order'] and Args['team_order'] ~= '' then
		local tlist = mw.text.split(Args['team_order'], '%s*[;,]%s*')
		for k, tname in ipairs(tlist) do
			if tname ~= '' then
				Args['team' .. k] = tname
			end
		end
	end
	if Args['team_header_note'] then
		notes_exist = true
		local note_string = frame:expandTemplate{title = 'efn',
			args = {group = 'lower-alpha', Args['team_header_note']}}
		team_header = team_header .. note_string
	end

-- Считаем количество команд подряд (игнорируем записи после пропуска места)
	ii_start = 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, один раз для фактического ранжирования, второй для поиска позиции во вложенных таблицах
-- Это возможно, потому что Lua допускает как числа, так и строки в качестве индексов.
		team_list[N_teams] = Args['team' .. N_teams] -- i^th entry is team X
		team_list[Args['team' .. N_teams]] = N_teams -- team X entry is position i
	end
	ii_end = N_teams
-- Get team to show
	local ii_show = team_list[Args['showteam']] -- nil if non-existant

-- Устанавливаем размер шрифта
	local font_size = Args['font_size'] or '100%'

-- Создаём таблицу
	table.insert(t, '{|class="wikitable" style="text-align:center; font-size:' .. font_size .. ';"\n')	-- Open table
-- Заголовок таблицы
	if Args['title'] then
		table.insert(t, '|+ ' .. Args['title'] .. '\n')
	end
-- First column
	t_return.count = 0		-- Dummy parameter, using subfunction call seems best at this point because both module are intertwined
	t_return.tab_text = t	-- Actual text
	t_return = p_sub.colhead(t_return, 'auto', team_header)
-- Other columns passed to subfunction
	t_return = p.header(t_return, Args, p_sub, N_teams, team_list, legs, multirowlegs)
	t = t_return.tab_text

-- Random value used for uniqueness
	math.randomseed( os.clock() * 10 ^ 8)
	local rand_val = math.random()
	local note_string, note_id
	local note_id_list = {}

-- Создаём строки
	ii_start = tonumber(Args['highest_row']) and (tonumber(Args['highest_row']) > top_pos) and tonumber(Args['highest_row']) or top_pos
	ii_end = tonumber(Args['lowest_row']) and (tonumber(Args['lowest_row']) < N_teams) and tonumber(Args['lowest_row']) or N_teams
	for ii = ii_start, ii_end do
-- Get team info
		team_code_ii = team_list[ii]
		team_name = Args['name_' .. team_code_ii] or team_code_ii
		local ii_style = 'text-align:' .. (Args['team_align'] or 'right') .. ';' .. 
		(ii and ii == ii_show and 'font-weight:bold;' or '') .. 
		(Args['team_nowrap'] and 'white-space:nowrap;' or '')
		local team_note = Args['note_' .. team_code_ii]
		if team_note then
			notes_exist = true
-- Только если существует
-- Сперва проверяем наличие ссылки на примечания
			if not Args['note_' .. team_note] then
-- It's the entry
-- Add random end for unique ID if more tables are present on article (which might otherwise share an ID)
				note_id = '"table_note_' .. team_code_ii .. rand_val .. '"'
				note_id_list[team_code_ii] = note_id
				note_string = frame:expandTemplate{title = 'efn',
					args = {group = 'lower-alpha', name = note_id, team_note}}
			else
-- Check for existence elsewhere
				local note_local_num = team_list[team_note] or ii_end + 1
				if note_id_list[team_note] or ((note_local_num >= ii_start) and (note_local_num <= ii_end)) then
-- It exists
					note_id = '"table_note_' .. team_note .. rand_val .. '"' -- Identifier
					note_string = frame:extensionTag{name = 'ref',
						args = {group = 'lower-alpha', name = note_id}}
				else
-- Now define the identifier for this
-- Add random end for unique ID
					note_id = '"table_note_' .. team_note .. rand_val .. '"'
					note_id_list[team_note] = note_id
					note_string = frame:expandTemplate{title = 'efn',
						args = {group = 'lower-alpha', name = note_id, Args['note_' .. team_note]}}
				end
			end
-- Now append this to the team_name string
			team_name = team_name .. note_string
		end
-- Названия команд
		table.insert(t, '|- \n')  -- New row
		table.insert(t, '| scope="row"' .. (multirowlegs and ' rowspan=' .. legs or '')
			.. 'style="' .. ii_style .. '"| ' .. team_name .. '\n')	-- Position number

-- Now include note to match results if needed
		for jj = top_pos, N_teams do
			local team_code_jj = team_list[jj]
			if ii == jj then
-- Nothing
			else
				for l = 1, legs do
					local m = (legs == 1) and 'match_' or 'match' .. l .. '_'
					local match_note = Args[m .. team_code_ii .. '_' .. team_code_jj .. '_note']
					if match_note then
						notes_exist = true
-- Только если существует
-- Сперва проверяем наличие ссылки на примечания
						if not (Args['note_' .. match_note] or Args[m .. match_note .. '_note']) then
-- It's the entry
-- Add random end for unique ID if more tables are present on article (which might otherwise share an ID)
							note_id = '"table_note_' .. l .. "_" .. team_code_ii .. '_' .. team_code_jj .. rand_val .. '"'
							note_id_list[team_code_ii .. '_' .. team_code_jj] = note_id
							note_string = frame:expandTemplate{title = 'efn',
								args = {group = 'lower-alpha', name = note_id, match_note}}
						else
-- Check for existence elsewhere
							local note_local_num = team_list[match_note] or ii_end + 1
							if note_id_list[match_note] then
-- Referencing an existing note
								note_id = note_id_list[match_note]	-- Borrow the existing identifier
								note_string = frame:extensionTag{name = 'ref',
									args = {group = 'lower-alpha', name = note_id}}
							elseif (note_local_num >= ii_start) and (note_local_num <= ii_end) then
-- Referencing a player note from a match note. In this case, we remove the leg part
								note_id = '"table_note_' .. match_note .. rand_val .. '"'
								note_string = frame:extensionTag{name = 'ref',
									args = {group = 'lower-alpha', name = note_id}}
							else
-- Referencing a different match note before its defined with its content
								note_id = '"table_note_' .. l .. "_" .. match_note .. rand_val .. '"'
								note_id_list[match_note] = note_id
								note_string = frame:expandTemplate{title = 'efn',
									args = {group = 'lower-alpha', name = note_id, Args['note_' .. match_note]}}
							end
						end
-- Now append this to the match result string
						Args[m .. team_code_ii .. '_' .. team_code_jj] = (Args[m .. team_code_ii .. '_' .. team_code_jj] or '–') .. note_string
					end
				end
			end
		end
-- Then individual results
		t = p.row(t, Args, N_teams, team_list, ii, ii_show, legs, multirowlegs)
	end

-- Закрываем таблицу
	table.insert(t, '|}\n')

-- Получаем инфирмацию для нижнего колонтитула
	local update = Args['update'] or '(дата неизвестна)'
	local start_date = Args['start_date'] or '(дата неизвестна)'
	local source = Args['source'] or frame:expandTemplate{title = 'нет источника',
		args = {reason = 'Источник не задан', date = os.date('%B %Y')}}

-- Создаём текст нижнего колонтитула
-- Обновление даты
	if string.lower(update) == 'complete' then
-- Ничего не делать
	elseif update == '' then
-- Пустой параметр
		table.insert(t_footer, 'Обновлено по состоянию на (дата неизвестна). ')
	elseif string.lower(update) == 'future' then
-- Дата начала будущих соревнований
		table.insert(t_footer, 'Первые матчи пройдут ' .. start_date .. '. ')
	else
		table.insert(t_footer, 'Обновлено по состоянию на ' .. update .. '. ')
	end
	table.insert(t_footer, 'Источник: ' .. source)
	if (Args['matches_style'] or '') == 'FBR' then
		table.insert(t_footer, Args['team_header']
			and '<br>Цвета: зелёный — победа команды в левой колонке; жёлтый — ничья; розовый — победа команды, занявшей первое место.'
			or '<br>Цвета: зелёный — победа хозяев; жёлтый — ничья; розовый — победа гостей.')
	elseif (Args['matches_style'] or '') == 'BSR' then
		table.insert(t_footer, Args['team_header']
			and '<br>Цвета: зелёный — победа команды в левой колонке; розовый — победа команды, занявшей первое место.'
			or '<br>Цвета: зелёный — победа хозяев; розовый — победа гостей.')
	end
	if Args['a_note'] then
		table.insert(t_footer, '<br>Для предстоящих матчей "м" означает наличие статьи о матче.')
	end
	if Args['ot_note'] then
		table.insert(t_footer, '<br>Матчи, результаты которых выделены более светлым оттенком фона завершились в дополнительное время.')
	end

-- Добавляем комментарии (если применимо)
	if notes_exist then
		table.insert(t_footer, '<br>Примечания:')
		t_footer = '<div class="reflist">' .. table.concat(t_footer) .. '</div>'
		t_footer = t_footer .. frame:expandTemplate{title = 'Комментарии',
			args = {group = 'lower-alpha'}}
	else
		t_footer = '<div class="reflist">' .. table.concat(t_footer) .. '</div>'
	end

-- Добавляем нижний колонтитул к основной таблице
	table.insert(t, t_footer)

-- Переопределяем якорь
	for k = 1, # t do
		if t[k]:match('%[%[#[^%[%]]*%|') then
			t[k] = mw.ustring.gsub(t[k], '(%[%[)(#[^%[%]]*%|)', '%1' .. baselink .. '%2')
		end
	end
	return '<div style="overflow:hidden">' .. '<div class="noresize overflowbugx" style="overflow:auto">\n' .. table.concat(t) .. '</div></div>'
end

-- Прочие функции
local function get_short_name(s, t, n, ss)
-- Возврат краткого названия если оно определено
	if s and s ~= '' then
		return s
	end
-- deflag if necessary
	if ss and n then
		if ss == 'noflag' then
			n = mw.ustring.gsub(n, '%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%]', '')
			n = mw.ustring.gsub(n, '^%s*&nbsp;%s*', '')
		elseif ss == 'flag' then
			n = mw.ustring.gsub(n, '(<span class="flagicon">%s*%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*link=)[^%|%[%]]*(%]%][^<>]*</span>)%s*%[%[([^%[%]%|]*)%|[^%[%]]*%]%]', '%1%3%2')
			n = mw.ustring.gsub(n, '(%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*link=)[^%|%[%]]*(%]%])%s*&nbsp;%s*%[%[([^%[%]%|]*)%|[^%[%]]*%]%]', '%1%3%2')
			n = mw.ustring.gsub(n, '(%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*link=)[^%|%[%]]*(%]%])%s*%[%[([^%[%]%|]*)%|[^%[%]]*%]%]', '%1%3%2')
			n = mw.ustring.gsub(n, '.*(<span class="flagicon">%s*%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%][^<>]*</span>).*', '%1')
			n = mw.ustring.gsub(n, '.*(%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%]).*', '%1')
			n = mw.ustring.gsub(n, '&nbsp;(</span>)', '%1')
		end
	end

-- Заменить текст ссылки в названии на team abbr если это возможно
	if n and t and n:match('(%[%[[^%[%]]*%]%])') then
		n = mw.ustring.gsub(n, '(%[%[[^%|%]]*%|)[^%|%]]*(%]%])', '%1' .. t .. '%2')
		n = mw.ustring.gsub(n, '(%[%[[^%|%]]*)(%]%])', '%1|' .. t .. '%2')
		n = mw.ustring.gsub(n, '(%[%[[^%|%]]*%|)([A-Z][A-Z][A-Z])(%]%])&nbsp;<span[^<>]*>%([A-Z][A-Z][A-Z]%)</span>', '%1%2%3')
		return n
	end
-- nothing worked, so just return the unlinked team abbr
	return t or ''
end

local function get_score_background(s, c)
	local s1, s2
-- Определяем цвета
	local wc, lc, tc
	if c == 'level2' then
		wc, lc, tc = '#ccf9cc', '#ffcccc', '#ffffcc' -- green2, red2, yellow2
	elseif c == 'level3' then
		wc, lc, tc = '#ddfcdd', '#ffdddd', '#ffffdd' -- green3, red3, yellow3
	elseif c == 'level4' then
		wc, lc, tc = '#eeffee', '#ffeeee', '#ffffee' -- green4, red4, yellow4
	else
		wc, lc, tc = '#bbf3bb', '#ffbbbb', '#ffffbb' -- green1, red1, yellow1
	end

-- Проверка наличия переопределений
	if s:match('^%s*<span%s+style%s*=["\'%s]*background[%-colr]*%s*:([^\'";<>]*).-$') then
		local c = mw.ustring.gsub(s, '^%s*<span%s+style%s*=["\'%s]*background[%-colr]*%s*:([^\'";<>]*).-$', '%1')
		return c
	end

-- Удалить ссылки при необходимости
	if s:match('^%s*%[%[[^%[%]]*%|([^%[%]]*)%]%]') then
		s = s:match('^%s*%[%[[^%[%]]*%|([^%[%]]*)%]%]')
	end
	if s:match('^%s*%[[^%[%]%s]*%s([^%[%]]*)%]') then
		s = s:match('^%s*%[[^%[%]%s]*%s([^%[%]]*)%]')
	end
	if s:match('<span[^<>]*>(.-)</span>') then
		s = s:match('<span[^<>]*>(.-)</span>')
	end

-- get the scores
	s1 = tonumber(mw.ustring.gsub( s or '', '^%s*([%d%.]+)%s*:%s*([%d%.]+).*', '%1') or '') or ''
	s2 = tonumber(mw.ustring.gsub( s or '', '^%s*([%d%.]+)%s*:%s*([%d%.]+).*', '%2') or '') or ''

-- Возвращаем окраску если возможно
	if s1 ~= '' and s2 ~= '' then
		return (s1 > s2) and wc or ((s2 > s1) and lc or tc)
	else
		return 'transparent'
	end
end

local function format_score(s)
	s = mw.ustring.gsub(s or '', '^%s*([%d%.]+)%s*[–−—%-]%s*([%d%.]+)', '%1:%2')
	s = mw.ustring.gsub(s, '^%s*([%d%.]+)%s*&[MmNn][Dd][Aa][Ss][Hh];%s*([%d%.]+)', '%1:%2')
	s = mw.ustring.gsub(s, '^%s*(%[%[[^%[%]]*%|[%d%.]+)%s*%-%s*([%d%.]+)', '%1:%2')
	s = mw.ustring.gsub(s, '^%s*(%[[^%[%]%s]*%s+[%d%.]+)%s*%-%s*([%d%.]+)', '%1:%2')
	s = mw.ustring.gsub(s, '^%s*(%[%[[^%[%]]*%|[%d%.]+)%s*&[MmNn][Dd][Aa][Ss][Hh];%s*([%d%.]+)', '%1:%2')
	s = mw.ustring.gsub(s, '^%s*(%[[^%[%]%s]*%s+[%d%.]+)%s*&[MmNn][Dd][Aa][Ss][Hh];%s*([%d%.]+)', '%1:%2')
	return s
end

function p.header(tt, Args, p_sub, N_teams, team_list, legs, multirowlegs)
	local ii, team_code_ii, short_name
	legs = legs or 1

-- Устанавливаем ширину столбщов
	local col_width = Args['match_col_width'] or '2.5em'

-- Get some default values in case it doesn't start at 1
	local top_pos = tonumber(Args['highest_pos']) or 1
	for l = 1, legs do
		if multirowlegs and l > 1 then
			break
		end
		for ii = top_pos, N_teams do
			team_code_ii = team_list[ii]
			short_name = get_short_name(Args['short_' .. team_code_ii], team_code_ii, Args['name_' .. team_code_ii], Args['short_style'] or '')
			local bl = legs > 1 and ii == top_pos and ' style="border-left:2px solid #aaa;"' or ''
			tt = p_sub.colhead(tt, col_width .. bl, short_name)
		end
	end
	return tt
end

function p.row(tt, Args, N_teams, team_list, ii, ii_show, legs, multirowlegs)
-- Note ii is the row number being shown
	local jj, fw, bg, result, result_extra, team_code_ii, team_code_jj
	legs = legs or 1

-- Определяем стили ячеек
	local matches_style = Args['matches_style'] or ''
	team_code_ii = team_list[ii]

-- Get some default values in case it doesn't start at 1
	local top_pos = tonumber(Args['highest_pos']) or 1
	for l = 1, legs do
		if multirowlegs and l > 1 then
			table.insert(tt, '|- \n')  -- New row
		end
		for jj = top_pos, N_teams do
			team_code_jj = team_list[jj]
			local m = (legs == 1) and 'match_' or 'match' .. l .. '_'
			result = Args[m .. team_code_ii .. '_' .. team_code_jj] or ''
			result_extra = Args['result_' .. team_code_ii .. '_' .. team_code_jj] or ''
			local bl = legs > 1 and jj == top_pos and 'border-left:2px solid #aaa;' or ''
			if ii == jj or result == 'null' then
-- Solid cell
				fw = 'font-weight:' .. (ii == ii_show and 'bold' or 'normal') .. ';'
				bg = 'background:transparent;'

-- Grey background color for solid cell
				if Args['solid_cell'] == 'grey' then
					table.insert(tt, '| style="' .. fw .. bl .. 'background:#bbb;" |\n')
				else
					table.insert(tt, '| style="' .. fw .. bl .. bg .. '" | &mdash;\n')
				end
			else
-- Content cell
-- Set bolding and background
				fw = 'font-weight:' .. ((ii == ii_show or jj == ii_show) and 'bold' or 'normal') .. ';'
				bg = 'background:transparent;'

-- Reformat dashes
				if result ~= '' then
					result = format_score(result)
				end
-- Background coloring if enabled
				if matches_style == 'FBR' and result ~= '' then
					if result_extra == 'OT' then
						bg = 'background:' .. get_score_background(result, 'level2') .. ';'
					elseif result_extra == 'PK' then
						bg = 'background:' .. get_score_background(result, 'level3') .. ';'
					else
						bg = 'background:' .. get_score_background(result, '') .. ';'
					end
				elseif matches_style == 'BSR' and result ~= '' then
					if result_extra == 'OT' then
						bg = 'background:' .. get_score_background(result, 'level3') .. ';'
					elseif result_extra == 'OTL' then
						bg = 'background:' .. get_score_background('0:1', 'level3') .. ';'
					elseif result_extra == 'OTW' then
						bg = 'background:' .. get_score_background('1:0', 'level3') .. ';'
					elseif result_extra == 'L' then
						bg = 'background:' .. get_score_background('0:1', '') .. ';'
					elseif result_extra == 'W' then
						bg = 'background:' .. get_score_background('1:0', '') .. ';'
					else
						bg = 'background:' .. get_score_background(result, '') .. ';'
					end
				end
				table.insert(tt, '| style="white-space:nowrap;' .. fw .. bl .. bg .. '" |' .. result .. '\n')
			end
		end
	end
	return tt
end

return p