Module:Bracket: Difference between revisions
From eSportsAmaze
More actions
Esportsamaze (talk | contribs) No edit summary |
Esportsamaze (talk | contribs) No edit summary |
||
| (12 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
-- ================================================================ | -- ================================================================ | ||
-- Module:Bracket | -- Module:Bracket v3.0 (Smart Nodes, Game Links, Info Popups) | ||
-- ================================================================ | -- ================================================================ | ||
local p = {} | |||
local p | local html = mw.html | ||
local html | |||
local cargo = mw.ext.cargo | local cargo = mw.ext.cargo | ||
local function esc(s) return s and s:gsub("\\", "\\\\"):gsub("'", "\\'") or "" end | |||
local function esc(s) | |||
end | |||
local function clean(s) | local function clean(s) | ||
if not s then return nil end | if not s then return nil end | ||
| Line 23: | Line 13: | ||
end | end | ||
-- | -- Smarter Date Formatter (Handles YYYY-MM-DD and YYYY-MM-DD HH:MM) | ||
local function formatDate(d) | |||
if not d or d == "" then return "" end | |||
local year, month, day, hour, min = string.match(d, "(%d%d%d%d)%-(%d%d)%-(%d%d)[T%s]*(%d*)%:*(%d*)") | |||
if year then | |||
local months = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"} | |||
local dateStr = tonumber(day) .. "-" .. months[tonumber(month)] | |||
if hour and hour ~= "" then | |||
dateStr = dateStr .. " " .. hour .. ":" .. (min ~= "" and min or "00") | |||
end | |||
return dateStr | |||
end | |||
return d | |||
end | |||
local teamLogoCache = {} | local teamLogoCache = {} | ||
local function getTeamLogo(teamName | local function getTeamLogo(teamName) | ||
if not teamName or teamName == "" then return | if not teamName or teamName == "" or teamName == "TBD" then return "" end | ||
if teamLogoCache[teamName] then return teamLogoCache[teamName] end | |||
if teamLogoCache[teamName] then | |||
local rows = cargo.query("Teams","image,image_dark",{where="name='"..esc(teamName).."'", limit=1}) | |||
local t = (rows and #rows > 0) and rows[1] or false | |||
if not t then | |||
teamLogoCache[teamName] = "" | |||
return "" | |||
end | end | ||
local light = t.image or "" | local light = t.image or "" | ||
local dark | local dark = (t.image_dark and t.image_dark ~= "") and t.image_dark or light | ||
if light == "" then return | if light == "" then | ||
teamLogoCache[teamName] = "" | |||
.. "[[File:"..dark .."| | return "" | ||
end | |||
local htmlStr = "[[File:"..light.."|18px|link=|class=logo-lightmode bk-team-logo]]" | |||
.. "[[File:"..dark .."|18px|link=|class=logo-darkmode bk-team-logo]]" | |||
teamLogoCache[teamName] = htmlStr | |||
return htmlStr | |||
end | end | ||
local function fetchMatch(event, matchID) | local function fetchMatch(event, matchID) | ||
if not event or event == "" then return {} end | |||
local rows = cargo.query("BracketMatch", | local rows = cargo.query("BracketMatch", | ||
"team1,score1,team2,score2,winner,bo,match_date,casters,vod,notes", | "team1,score1,team2,score2,winner,bo,match_date,casters,vod,notes,short1,short2", | ||
{ where = "event='"..esc(event).."' AND match_id='"..esc(matchID).."'", | { where = "event='"..esc(event).."' AND match_id='"..esc(matchID).."'", limit = 1 }) | ||
if rows and #rows > 0 then return rows[1] end | if rows and #rows > 0 then return rows[1] end | ||
return {} | return {} | ||
end | end | ||
local function mergeMatchData(cargoData, args, prefix) | local function mergeMatchData(cargoData, args, prefix) | ||
local d = {} | local d = {} | ||
d.label = clean(args[prefix.."label"]) | |||
d.team1 = clean(args[prefix.."team1"]) or clean(cargoData.team1) or "TBD" | d.team1 = clean(args[prefix.."team1"]) or clean(cargoData.team1) or "TBD" | ||
d.team2 = clean(args[prefix.."team2"]) or clean(cargoData.team2) or "TBD" | d.team2 = clean(args[prefix.."team2"]) or clean(cargoData.team2) or "TBD" | ||
d.short1 = clean(args[prefix.."short1"]) or clean(cargoData.short1) or d.team1 | |||
d.short2 = clean(args[prefix.."short2"]) or clean(cargoData.short2) or d.team2 | |||
d.score1 = clean(args[prefix.."score1"]) or clean(cargoData.score1) or "" | d.score1 = clean(args[prefix.."score1"]) or clean(cargoData.score1) or "" | ||
d.score2 = clean(args[prefix.."score2"]) or clean(cargoData.score2) or "" | d.score2 = clean(args[prefix.."score2"]) or clean(cargoData.score2) or "" | ||
| Line 76: | Line 80: | ||
end | end | ||
-- ── | -- ── NEW MODERN MATCH CARD ── | ||
-- | local function renderMatchCard(d, matchID, defaultLabel, winTo, loseTo, target, isDrop, extraClass, game) | ||
-- | local box = html.create('div'):addClass('bk-match') | ||
-- | :attr('id', 'match-' .. matchID) | ||
if winTo and winTo ~= "" then box:attr('data-target-win', 'match-' .. winTo) end | |||
if loseTo and loseTo ~= "" then box:attr('data-target-lose', 'match-' .. loseTo) end | |||
if target and target ~= "" then box:attr('data-target', 'match-' .. target) end | |||
if isDrop then box:attr('data-drop', 'true') end | |||
if extraClass then box:addClass(extraClass) end | |||
local win1 = (d.winner == "1" or d.winner == d.team1) and d.team1 ~= "TBD" | local win1 = (d.winner == "1" or d.winner == d.team1) and d.team1 ~= "TBD" | ||
local win2 = (d.winner == "2" or d.winner == d.team2) and d.team2 ~= "TBD" | local win2 = (d.winner == "2" or d.winner == d.team2) and d.team2 ~= "TBD" | ||
-- | -- Top Bar (Entire bar is now the clickable trigger) | ||
local top = box:tag('div'):addClass('bk-match-top') | |||
:attr('data-matchid', matchID) -- Ready for your future Match Page logic | |||
:attr('data-team1', d.team1):attr('data-team2', d.team2) | |||
:attr('data-score1', d.score1):attr('data-score2', d.score2) | |||
:attr('data-bo', d.bo):attr('data-date', d.date) | |||
:attr('data-casters', d.casters):attr('data-vod', d.vod):attr('data-notes', d.notes) | |||
:attr('title', 'Click for Match Details') | |||
-- The Label | |||
top:tag('span'):addClass('bk-match-label') | |||
:wikitext(d.label or defaultLabel or matchID) | |||
-- Meta Info (ONLY Date) | |||
local metaStr = formatDate(d.date) | |||
top:tag('span'):addClass('bk-match-meta'):wikitext(metaStr) | |||
-- | -- Teams Wrapper | ||
local teamsWrap = box:tag('div'):addClass('bk-teams-wrap') | |||
local linkPrefix = "" | |||
if game and game ~= "" and game ~= "BGMI" then linkPrefix = game .. "/Teams/" end | |||
-- Team 1 | -- Team 1 | ||
local row1 = | local row1 = teamsWrap:tag('div'):addClass('bk-team') | ||
if win1 then row1:addClass('bk-win') | if win1 then row1:addClass('bk-win') elseif win2 then row1:addClass('bk-lose') end | ||
row1:wikitext(getTeamLogo(d.team1)) | |||
local t1Link = d.team1 ~= "TBD" and "[[" .. linkPrefix .. d.team1 .. "|" .. d.short1 .. "]]" or "TBD" | |||
local | row1:tag('span'):addClass('bk-team-name'):wikitext(t1Link) | ||
row1:tag('span'):addClass('bk-score'):wikitext(d.score1 ~= "" and d.score1 or "-") | |||
-- Team 2 | -- Team 2 | ||
local row2 = | local row2 = teamsWrap:tag('div'):addClass('bk-team') | ||
if win2 then row2:addClass('bk-win') | if win2 then row2:addClass('bk-win') elseif win1 then row2:addClass('bk-lose') end | ||
row2:wikitext(getTeamLogo(d.team2)) | |||
local t2Link = d.team2 ~= "TBD" and "[[" .. linkPrefix .. d.team2 .. "|" .. d.short2 .. "]]" or "TBD" | |||
local | row2:tag('span'):addClass('bk-team-name'):wikitext(t2Link) | ||
row2:tag('span'):addClass('bk-score'):wikitext(d.score2 ~= "" and d.score2 or "-") | |||
return | return box | ||
end | end | ||
-- | -- ================================================================ | ||
-- FORMAT | -- FORMAT: SINGLE ELIMINATION | ||
-- | -- ================================================================ | ||
local function renderSingleElim(args, event, teamCount, game) | |||
local function renderSingleElim(args, event, teamCount) | |||
local rounds = math.floor(math.log(teamCount) / math.log(2)) | local rounds = math.floor(math.log(teamCount) / math.log(2)) | ||
local suffixNames = { "Grand Final", "Semifinals", "Quarterfinals", "Round of 16", "Round of 32" } | |||
local suffixNames = { "Grand Final", " | |||
local scroll = html.create('div'):addClass('bk-scroll') | local scroll = html.create('div'):addClass('bk-scroll') | ||
local wrapper = scroll:tag('div'):addClass('bk-wrapper | local wrapper = scroll:tag('div'):addClass('bk-wrapper') | ||
for r = 1, rounds do | for r = 1, rounds do | ||
local col = wrapper:tag('div'):addClass('bk-col | local col = wrapper:tag('div'):addClass('bk-col') | ||
col:tag('div'):addClass('bk-col-header'):wikitext( | local pos = rounds - r + 1 | ||
col:tag('div'):addClass('bk-col-header'):wikitext(args["r"..r.."name"] or suffixNames[pos] or ("Round "..r)) | |||
local matchCount = teamCount / (2^r) | local matchCount = teamCount / (2^r) | ||
| Line 186: | Line 156: | ||
local cd = fetchMatch(event, matchID) | local cd = fetchMatch(event, matchID) | ||
local d = mergeMatchData(cd, args, matchID) | local d = mergeMatchData(cd, args, matchID) | ||
local target = "" | |||
local isFinal = (r == rounds) | |||
if not isFinal then | |||
local nextMatchNum = math.ceil(m / 2) | |||
target = "R"..(r+1).."M"..nextMatchNum | |||
end | |||
local | local card = renderMatchCard(d, matchID, "Match "..m, nil, nil, target, false, isFinal and "bk-final-match" or "", game) | ||
col:node(card) | |||
end | end | ||
end | end | ||
return tostring(scroll) | return tostring(scroll) | ||
end | end | ||
-- | -- ================================================================ | ||
-- FORMAT: CUSTOM MANUAL BRACKET | |||
-- ================================================================ | |||
local function renderCustom(args, event, game) | |||
local scroll = html.create('div'):addClass('bk-scroll') | |||
local wrapper = scroll:tag('div'):addClass('bk-wrapper') | |||
local cols = tonumber(args.columns) or 2 | |||
for c = 1, cols do | |||
local col = wrapper:tag('div'):addClass('bk-col') | |||
local colName = clean(args["col"..c.."name"]) | |||
if colName and colName:lower() ~= "none" then | |||
col:tag('div'):addClass('bk-col-header'):wikitext(colName) | |||
-- FORMAT | |||
-- | |||
local scroll | |||
local wrapper = scroll:tag('div'):addClass('bk-wrapper | |||
local | |||
for | |||
local | |||
end | end | ||
local matchesStr = args["col"..c.."_matches"] | |||
if matchesStr then | |||
for mID in string.gmatch(matchesStr, '([^,]+)') do | |||
mID = clean(mID) | |||
local cd = fetchMatch(event, mID) | |||
local d = mergeMatchData(cd, args, mID) | |||
local winTo = args[mID.."_win_to"] | |||
local loseTo = args[mID.."_lose_to"] | |||
local target = args[mID.."_target"] | |||
local isDrop = args[mID.."_drop"] == "true" | |||
col:node(renderMatchCard(d, mID, args[mID.."_label"], winTo, loseTo, target, isDrop, "", game)) | |||
local | |||
local | |||
local | |||
end | end | ||
end | end | ||
end | end | ||
return tostring(scroll) | return tostring(scroll) | ||
end | end | ||
-- | -- ================================================================ | ||
-- MAIN ENTRY | |||
-- ================================================================ | |||
-- MAIN ENTRY | |||
-- | |||
function p.main(frame) | function p.main(frame) | ||
local args = (frame:getParent() and frame:getParent().args) or frame.args | local args = (frame:getParent() and frame:getParent().args) or frame.args | ||
local format = (clean(args.format) or "single"):lower() | |||
local format | local event = clean(args.event) or "" | ||
local event = clean(args. | local game = clean(args.game) or "" -- Pass the game prefix (e.g. "Honor of Kings") | ||
local teamCount = tonumber(args.teams) or 8 | local teamCount = tonumber(args.teams) or 8 | ||
local root = html.create('div'):addClass('bk-root') | local root = html.create('div'):addClass('bk-root') | ||
local bracketHTML | local bracketHTML | ||
if format == "single" then | |||
if format == "single | bracketHTML = renderSingleElim(args, event, teamCount, game) | ||
bracketHTML = renderSingleElim(args, event, teamCount | elseif format == "custom" then | ||
bracketHTML = renderCustom(args, event, game) | |||
elseif format == " | |||
bracketHTML = | |||
else | else | ||
bracketHTML = "<div | bracketHTML = "<div style='color:red; padding:20px'>Format '"..format.."' is currently being upgraded to v3.0. Use 'single' or 'custom' for now.</div>" | ||
end | end | ||
root:wikitext(bracketHTML) | root:wikitext(bracketHTML) | ||
-- | -- MODAL POPUP | ||
root: | local overlay = root:tag('div'):addClass('bk-modal-overlay') | ||
local modal = overlay:tag('div'):addClass('bk-modal') | |||
modal:tag('span'):addClass('bk-modal-close'):attr('role','button'):wikitext('✕') | |||
local mheader = modal:tag('div'):addClass('bk-modal-header') | |||
local mteams = mheader:tag('div'):addClass('bk-modal-teams') | |||
mteams:tag('span'):addClass('bk-modal-t1') | |||
mteams:tag('span'):addClass('bk-modal-vs'):wikitext('vs') | |||
mteams:tag('span'):addClass('bk-modal-t2') | |||
local mscore = mheader:tag('div'):addClass('bk-modal-score') | |||
mscore:tag('span'):addClass('bk-modal-s1') | |||
mscore:tag('span'):addClass('bk-modal-dash'):wikitext('–') | |||
mscore:tag('span'):addClass('bk-modal-s2') | |||
local mmeta = modal:tag('div'):addClass('bk-modal-meta') | |||
mmeta:tag('div'):addClass('bk-modal-row bk-modal-bo') | |||
mmeta:tag('div'):addClass('bk-modal-row bk-modal-date') | |||
mmeta:tag('div'):addClass('bk-modal-row bk-modal-casters') | |||
mmeta:tag('div'):addClass('bk-modal-row bk-modal-notes') | |||
mmeta:tag('div'):addClass('bk-modal-row bk-modal-vod') | |||
return tostring(root) | return tostring(root) | ||
end | end | ||
return p | return p | ||
Latest revision as of 03:39, 4 April 2026
Documentation for this module may be created at Module:Bracket/doc
-- ================================================================
-- Module:Bracket v3.0 (Smart Nodes, Game Links, Info Popups)
-- ================================================================
local p = {}
local html = mw.html
local cargo = mw.ext.cargo
local function esc(s) return s and s:gsub("\\", "\\\\"):gsub("'", "\\'") or "" end
local function clean(s)
if not s then return nil end
local c = s:gsub("[\r\n]",""):gsub("^%s*(.-)%s*$","%1")
return c ~= "" and c or nil
end
-- Smarter Date Formatter (Handles YYYY-MM-DD and YYYY-MM-DD HH:MM)
local function formatDate(d)
if not d or d == "" then return "" end
local year, month, day, hour, min = string.match(d, "(%d%d%d%d)%-(%d%d)%-(%d%d)[T%s]*(%d*)%:*(%d*)")
if year then
local months = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}
local dateStr = tonumber(day) .. "-" .. months[tonumber(month)]
if hour and hour ~= "" then
dateStr = dateStr .. " " .. hour .. ":" .. (min ~= "" and min or "00")
end
return dateStr
end
return d
end
local teamLogoCache = {}
local function getTeamLogo(teamName)
if not teamName or teamName == "" or teamName == "TBD" then return "" end
if teamLogoCache[teamName] then return teamLogoCache[teamName] end
local rows = cargo.query("Teams","image,image_dark",{where="name='"..esc(teamName).."'", limit=1})
local t = (rows and #rows > 0) and rows[1] or false
if not t then
teamLogoCache[teamName] = ""
return ""
end
local light = t.image or ""
local dark = (t.image_dark and t.image_dark ~= "") and t.image_dark or light
if light == "" then
teamLogoCache[teamName] = ""
return ""
end
local htmlStr = "[[File:"..light.."|18px|link=|class=logo-lightmode bk-team-logo]]"
.. "[[File:"..dark .."|18px|link=|class=logo-darkmode bk-team-logo]]"
teamLogoCache[teamName] = htmlStr
return htmlStr
end
local function fetchMatch(event, matchID)
if not event or event == "" then return {} end
local rows = cargo.query("BracketMatch",
"team1,score1,team2,score2,winner,bo,match_date,casters,vod,notes,short1,short2",
{ where = "event='"..esc(event).."' AND match_id='"..esc(matchID).."'", limit = 1 })
if rows and #rows > 0 then return rows[1] end
return {}
end
local function mergeMatchData(cargoData, args, prefix)
local d = {}
d.label = clean(args[prefix.."label"])
d.team1 = clean(args[prefix.."team1"]) or clean(cargoData.team1) or "TBD"
d.team2 = clean(args[prefix.."team2"]) or clean(cargoData.team2) or "TBD"
d.short1 = clean(args[prefix.."short1"]) or clean(cargoData.short1) or d.team1
d.short2 = clean(args[prefix.."short2"]) or clean(cargoData.short2) or d.team2
d.score1 = clean(args[prefix.."score1"]) or clean(cargoData.score1) or ""
d.score2 = clean(args[prefix.."score2"]) or clean(cargoData.score2) or ""
d.winner = clean(args[prefix.."win"]) or clean(cargoData.winner) or ""
d.bo = clean(args[prefix.."bo"]) or clean(cargoData.bo) or ""
d.date = clean(args[prefix.."date"]) or clean(cargoData.match_date) or ""
d.casters = clean(args[prefix.."casters"]) or clean(cargoData.casters) or ""
d.vod = clean(args[prefix.."vod"]) or clean(cargoData.vod) or ""
d.notes = clean(args[prefix.."notes"]) or clean(cargoData.notes) or ""
return d
end
-- ── NEW MODERN MATCH CARD ──
local function renderMatchCard(d, matchID, defaultLabel, winTo, loseTo, target, isDrop, extraClass, game)
local box = html.create('div'):addClass('bk-match')
:attr('id', 'match-' .. matchID)
if winTo and winTo ~= "" then box:attr('data-target-win', 'match-' .. winTo) end
if loseTo and loseTo ~= "" then box:attr('data-target-lose', 'match-' .. loseTo) end
if target and target ~= "" then box:attr('data-target', 'match-' .. target) end
if isDrop then box:attr('data-drop', 'true') end
if extraClass then box:addClass(extraClass) end
local win1 = (d.winner == "1" or d.winner == d.team1) and d.team1 ~= "TBD"
local win2 = (d.winner == "2" or d.winner == d.team2) and d.team2 ~= "TBD"
-- Top Bar (Entire bar is now the clickable trigger)
local top = box:tag('div'):addClass('bk-match-top')
:attr('data-matchid', matchID) -- Ready for your future Match Page logic
:attr('data-team1', d.team1):attr('data-team2', d.team2)
:attr('data-score1', d.score1):attr('data-score2', d.score2)
:attr('data-bo', d.bo):attr('data-date', d.date)
:attr('data-casters', d.casters):attr('data-vod', d.vod):attr('data-notes', d.notes)
:attr('title', 'Click for Match Details')
-- The Label
top:tag('span'):addClass('bk-match-label')
:wikitext(d.label or defaultLabel or matchID)
-- Meta Info (ONLY Date)
local metaStr = formatDate(d.date)
top:tag('span'):addClass('bk-match-meta'):wikitext(metaStr)
-- Teams Wrapper
local teamsWrap = box:tag('div'):addClass('bk-teams-wrap')
local linkPrefix = ""
if game and game ~= "" and game ~= "BGMI" then linkPrefix = game .. "/Teams/" end
-- Team 1
local row1 = teamsWrap:tag('div'):addClass('bk-team')
if win1 then row1:addClass('bk-win') elseif win2 then row1:addClass('bk-lose') end
row1:wikitext(getTeamLogo(d.team1))
local t1Link = d.team1 ~= "TBD" and "[[" .. linkPrefix .. d.team1 .. "|" .. d.short1 .. "]]" or "TBD"
row1:tag('span'):addClass('bk-team-name'):wikitext(t1Link)
row1:tag('span'):addClass('bk-score'):wikitext(d.score1 ~= "" and d.score1 or "-")
-- Team 2
local row2 = teamsWrap:tag('div'):addClass('bk-team')
if win2 then row2:addClass('bk-win') elseif win1 then row2:addClass('bk-lose') end
row2:wikitext(getTeamLogo(d.team2))
local t2Link = d.team2 ~= "TBD" and "[[" .. linkPrefix .. d.team2 .. "|" .. d.short2 .. "]]" or "TBD"
row2:tag('span'):addClass('bk-team-name'):wikitext(t2Link)
row2:tag('span'):addClass('bk-score'):wikitext(d.score2 ~= "" and d.score2 or "-")
return box
end
-- ================================================================
-- FORMAT: SINGLE ELIMINATION
-- ================================================================
local function renderSingleElim(args, event, teamCount, game)
local rounds = math.floor(math.log(teamCount) / math.log(2))
local suffixNames = { "Grand Final", "Semifinals", "Quarterfinals", "Round of 16", "Round of 32" }
local scroll = html.create('div'):addClass('bk-scroll')
local wrapper = scroll:tag('div'):addClass('bk-wrapper')
for r = 1, rounds do
local col = wrapper:tag('div'):addClass('bk-col')
local pos = rounds - r + 1
col:tag('div'):addClass('bk-col-header'):wikitext(args["r"..r.."name"] or suffixNames[pos] or ("Round "..r))
local matchCount = teamCount / (2^r)
for m = 1, matchCount do
local matchID = "R"..r.."M"..m
local cd = fetchMatch(event, matchID)
local d = mergeMatchData(cd, args, matchID)
local target = ""
local isFinal = (r == rounds)
if not isFinal then
local nextMatchNum = math.ceil(m / 2)
target = "R"..(r+1).."M"..nextMatchNum
end
local card = renderMatchCard(d, matchID, "Match "..m, nil, nil, target, false, isFinal and "bk-final-match" or "", game)
col:node(card)
end
end
return tostring(scroll)
end
-- ================================================================
-- FORMAT: CUSTOM MANUAL BRACKET
-- ================================================================
local function renderCustom(args, event, game)
local scroll = html.create('div'):addClass('bk-scroll')
local wrapper = scroll:tag('div'):addClass('bk-wrapper')
local cols = tonumber(args.columns) or 2
for c = 1, cols do
local col = wrapper:tag('div'):addClass('bk-col')
local colName = clean(args["col"..c.."name"])
if colName and colName:lower() ~= "none" then
col:tag('div'):addClass('bk-col-header'):wikitext(colName)
end
local matchesStr = args["col"..c.."_matches"]
if matchesStr then
for mID in string.gmatch(matchesStr, '([^,]+)') do
mID = clean(mID)
local cd = fetchMatch(event, mID)
local d = mergeMatchData(cd, args, mID)
local winTo = args[mID.."_win_to"]
local loseTo = args[mID.."_lose_to"]
local target = args[mID.."_target"]
local isDrop = args[mID.."_drop"] == "true"
col:node(renderMatchCard(d, mID, args[mID.."_label"], winTo, loseTo, target, isDrop, "", game))
end
end
end
return tostring(scroll)
end
-- ================================================================
-- MAIN ENTRY
-- ================================================================
function p.main(frame)
local args = (frame:getParent() and frame:getParent().args) or frame.args
local format = (clean(args.format) or "single"):lower()
local event = clean(args.event) or ""
local game = clean(args.game) or "" -- Pass the game prefix (e.g. "Honor of Kings")
local teamCount = tonumber(args.teams) or 8
local root = html.create('div'):addClass('bk-root')
local bracketHTML
if format == "single" then
bracketHTML = renderSingleElim(args, event, teamCount, game)
elseif format == "custom" then
bracketHTML = renderCustom(args, event, game)
else
bracketHTML = "<div style='color:red; padding:20px'>Format '"..format.."' is currently being upgraded to v3.0. Use 'single' or 'custom' for now.</div>"
end
root:wikitext(bracketHTML)
-- MODAL POPUP
local overlay = root:tag('div'):addClass('bk-modal-overlay')
local modal = overlay:tag('div'):addClass('bk-modal')
modal:tag('span'):addClass('bk-modal-close'):attr('role','button'):wikitext('✕')
local mheader = modal:tag('div'):addClass('bk-modal-header')
local mteams = mheader:tag('div'):addClass('bk-modal-teams')
mteams:tag('span'):addClass('bk-modal-t1')
mteams:tag('span'):addClass('bk-modal-vs'):wikitext('vs')
mteams:tag('span'):addClass('bk-modal-t2')
local mscore = mheader:tag('div'):addClass('bk-modal-score')
mscore:tag('span'):addClass('bk-modal-s1')
mscore:tag('span'):addClass('bk-modal-dash'):wikitext('–')
mscore:tag('span'):addClass('bk-modal-s2')
local mmeta = modal:tag('div'):addClass('bk-modal-meta')
mmeta:tag('div'):addClass('bk-modal-row bk-modal-bo')
mmeta:tag('div'):addClass('bk-modal-row bk-modal-date')
mmeta:tag('div'):addClass('bk-modal-row bk-modal-casters')
mmeta:tag('div'):addClass('bk-modal-row bk-modal-notes')
mmeta:tag('div'):addClass('bk-modal-row bk-modal-vod')
return tostring(root)
end
return p