Модуль:Bracket

Материал из Наро-Фоминский Футбол
Перейти к навигации Перейти к поиску

Для документации этого модуля может быть создана страница Модуль:Bracket/doc

-- Модуль:Bracket
-- Турнирная сетка на выбывание: 2–5 этапов, 1–N игр на этапе,
-- матч за 3-е место, даты, места, свои названия этапов.

local p = {}

-- Стандартные названия этапов по количеству этапов
local defaultNames = {
    [2] = { "Полуфиналы", "Финал" },
    [3] = { "Четвертьфиналы", "Полуфиналы", "Финал" },
    [4] = { "1/8 финала", "Четвертьфиналы", "Полуфиналы", "Финал" },
    [5] = { "1/16 финала", "1/8 финала", "Четвертьфиналы", "Полуфиналы", "Финал" },
}

local function preproc(frame, s)
    if s == nil then return nil end
    s = mw.text.trim(s)
    if s == "" then return nil end
    return frame:preprocess(s)
end

local function isYes(s)
    s = s and mw.ustring.lower(mw.text.trim(s)) or ""
    return s == "yes" or s == "да" or s == "1" or s == "true"
end

-- Рендер одной игры. prefix: "r1m2" (этап 1, игра 2) или "third"
local function matchHtml(frame, prefix, args)
    local function prm(name) return preproc(frame, args[prefix .. name]) end

    local t1, t2 = prm("team1"), prm("team2")
    local s1, s2 = prm("team1score"), prm("team2score")
    local n1, n2 = prm("team1note"), prm("team2note")
    local date, venue = prm("date"), prm("venue")
    local winner = prm("winner")

    if not (t1 or t2 or s1 or s2) then return nil end

    -- Победитель: явно или по числовому счёту
    if winner ~= "1" and winner ~= "2" then
        local a, b = tonumber(s1), tonumber(s2)
        if a and b then
            if a > b then winner = "1"
            elseif b > a then winner = "2"
            else winner = nil end
        else
            winner = nil
        end
    end

    local meta = ""
    if date or venue then
        local parts = {}
        if date  then parts[#parts + 1] = '<span class="bracket-date">'  .. date  .. '</span>' end
        if venue then parts[#parts + 1] = '<span class="bracket-venue">' .. venue .. '</span>' end
        meta = '<div class="bracket-matchmeta">' .. table.concat(parts, " — ") .. '</div>'
    end

    local function row(t, s, note, isWinner)
        local cls = "bracket-team"
        if isWinner then cls = cls .. " bracket-winner" end
        local nameCell = t or "&nbsp;"
        if note then
            nameCell = nameCell .. ' <span class="bracket-note">(' .. note .. ')</span>'
        end
        return '<tr class="' .. cls .. '"><td class="bracket-name">' .. nameCell ..
               '</td><td class="bracket-score">' .. (s or "") .. '</td></tr>'
    end

    return meta ..
           '<table class="bracket-matchteams">' ..
           row(t1, s1, n1, winner == "1") ..
           row(t2, s2, n2, winner == "2") ..
           '</table>'
end

function p.main(frame)
    local args = frame:getParent().args
    local n = tonumber(mw.text.trim(args.rounds or ""))
    if not n or n < 2 or n > 5 then
        error("Bracket: параметр «rounds» должен быть числом от 2 до 5")
    end

    local out = {}
    local function w(s) out[#out + 1] = s end

    w('<div class="bracket"><div class="bracket-inner">')

    for r = 1, n do
        local title = preproc(frame, args["r" .. r .. "name"]) or defaultNames[n][r]

        -- Собираем игры этапа
        local matches = {}
        local m = 1
        while m <= 32 do
            local html = matchHtml(frame, "r" .. r .. "m" .. m, args)
            if not html then break end
            matches[#matches + 1] = html
            m = m + 1
        end
        local c = #matches

        w('<div class="bracket-round">')
        w('<div class="bracket-roundtitle">' .. title .. '</div>')
        w('<div class="bracket-roundbody">')
        -- Ведущий разделитель (для вертикального центрирования)
        w('<div class="bracket-gap"></div>')
        for i = 1, c do
            local html = matches[i]
            local cls = "bracket-match"
            if r < n then
                if c == 1 or (i % 2 == 1 and i == c) then
                    cls = cls .. " bracket-m-solo"
                elseif i % 2 == 1 then
                    cls = cls .. " bracket-m-up"
                else
                    cls = cls .. " bracket-m-lo"
                end
            end
            w('<div class="bracket-slot"><div class="' .. cls .. '">' .. html .. '</div></div>')
            if i < c then
                local gcls = "bracket-gap"
                if r < n and i % 2 == 1 then
                    gcls = gcls .. " bracket-gapline"
                end
                w('<div class="' .. gcls .. '"></div>')
            end
        end
        w('<div class="bracket-gap"></div>')
        w('</div></div>')
    end

    w('</div>') -- /bracket-inner

    if isYes(args.thirdplace) then
        local html = matchHtml(frame, "third", args)
        local t = preproc(frame, args.thirdname) or "Матч за третье место"
        w('<div class="bracket-third">')
        w('<div class="bracket-roundtitle bracket-thirdtitle">' .. t .. '</div>')
        w(html or "")
        w('</div>')
    end

    w('</div>')
    return table.concat(out)
end

return p