[checked revision] | [pending revision] |
m (Syncing content across wikis, if something seems broken as a result let me know) |
m (Syncing content across wikis, if something seems broken as a result let me know) |
||
(One intermediate revision by the same user not shown) | |||
Line 1: | Line 1: | ||
− | local util_table = require('Module:TableUtil') |
||
local util_args = require('Module:ArgsUtil') |
local util_args = require('Module:ArgsUtil') |
||
+ | local util_cargo = require('Module:CargoUtil') |
||
+ | local util_esports = require('Module:EsportsUtil') |
||
+ | local util_table = require('Module:TableUtil') |
||
+ | local util_text = require('Module:TextUtil') |
||
+ | local util_vars = require('Module:VarsUtil') |
||
local bracket_wiki = require('Module:Bracket/Wiki') -- wiki localization per game |
local bracket_wiki = require('Module:Bracket/Wiki') -- wiki localization per game |
||
+ | |||
+ | local m_team = require('Module:Team') |
||
+ | local lang = mw.getLanguage('en') |
||
+ | |||
+ | local ROWS_PER_TEAM = 6 |
||
+ | local ROWS_PER_TITLE = 2 |
||
+ | local ROWS_PER_HLINE = 1 |
||
+ | local ROUNDWIDTH = 12 |
||
+ | local LINEWIDTH = '3em' |
||
+ | local SCOREWIDTH = 2 |
||
+ | |||
+ | local sep = '%s*,%s*' |
||
local h = {} |
local h = {} |
||
Line 9: | Line 25: | ||
local args = {} |
local args = {} |
||
for k, v in pairs(tpl_args) do |
for k, v in pairs(tpl_args) do |
||
− | if |
+ | if type(k) ~= 'string' then |
+ | -- pass |
||
− | local match, team, val = k:match('(R%d+M%d+)_(.*)_(.*)') |
||
− | + | elseif k:find('R%d+M%d+_.*_') then |
|
+ | local r, m, val, team = k:match('R(%d+)M(%d+)_(.*)_(%d+)') |
||
− | args[match] = { team1 = {}, team2 = {} } |
||
+ | r = tonumber(r) |
||
+ | m = tonumber(m) |
||
+ | h.initializeMatch(args, r, m) |
||
+ | if val == 'team' then |
||
+ | args[r][m]['team' .. team][val] = m_team.teamlinkname(v) |
||
+ | else |
||
+ | args[r][m]['team' .. team][val] = v |
||
end |
end |
||
− | args[match][team][val] = v |
||
elseif k:find('R%d+M%d+_.*') then |
elseif k:find('R%d+M%d+_.*') then |
||
− | local |
+ | local r, m, val = k:match('R(%d+)M(%d+)_(.*)') |
+ | r = tonumber(r) |
||
− | if not args[match] then |
||
+ | m = tonumber(m) |
||
− | args[match] = { team1 = {}, team2 = {} } |
||
+ | h.initializeMatch(args, r, m) |
||
− | end |
||
− | args[ |
+ | args[r][m][val] = v |
+ | elseif k:find('R%d+_') then |
||
+ | local r, val = k:match('R(%d+)_(.*)') |
||
+ | r = tonumber(r) |
||
+ | h.initializeMatch(args, r) |
||
+ | args[r][val] = v |
||
else |
else |
||
args[k] = v |
args[k] = v |
||
Line 28: | Line 55: | ||
end |
end |
||
− | function h. |
+ | function h.initializeMatch(args, r, m) |
+ | if not args[r] then |
||
− | local tbl = mw.html.create('div') |
||
+ | args[r] = {} |
||
− | :addClass('bracket-grid') |
||
+ | end |
||
+ | if not args[r][m] and m then |
||
+ | args[r][m] = { team1 = {}, team2 = {} } |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function h.processSettings(settings, args) |
||
+ | -- in theory this could be done in the settings module before returning but |
||
+ | -- this way the code is a bit more hidden from users editing stuff |
||
+ | -- and also this makes the settings module closer to a read-only table that you |
||
+ | -- import (and clone) here which i guess is nice? |
||
+ | -- tbh im not sure if this was the right way to do it tho |
||
+ | for r, col in ipairs(settings) do |
||
+ | local m = #col.matches |
||
+ | while m >= 1 do |
||
+ | -- need to iterate backwards bc we'll delete third-place matches if hidden |
||
+ | local match = col.matches[m] |
||
+ | local lines = col.lines and col.lines[m] |
||
+ | if lines and lines.reseed then |
||
+ | lines.class = lines.class:format(lang:lc(args.reseed or 'reseeding')) |
||
+ | end |
||
+ | if match.argtoshow then |
||
+ | if not util_args.castAsBool(args[match.argtoshow]) then |
||
+ | if col.matches[m+1] then |
||
+ | col.matches[m+1].above = (col.matches[m+1].above or 0) + (match.above or 0) + 6 |
||
+ | end |
||
+ | table.remove(col.matches,m) |
||
+ | end |
||
+ | end |
||
+ | m = m - 1 |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | -- cargo |
||
+ | function h.addCargoData(args, settings) |
||
+ | local overviewPage = util_esports.getOverviewPage(args.page) |
||
+ | local data = h.doCargoQuery(overviewPage) |
||
+ | if not next(data) then |
||
+ | return |
||
+ | end |
||
+ | local processed = h.processCargoData(data) |
||
+ | h.addProcessedToArgs(args, settings, processed, overviewPage) |
||
+ | end |
||
+ | |||
+ | function h.doCargoQuery(page) |
||
+ | local query = { |
||
+ | tables = 'MatchSchedule', |
||
+ | fields = { |
||
+ | 'Team1', |
||
+ | 'Team2', |
||
+ | 'Team1Final', |
||
+ | 'Team2Final', |
||
+ | 'Winner', |
||
+ | 'FF', |
||
+ | 'Team1Score', |
||
+ | 'Team2Score', |
||
+ | 'Tab', |
||
+ | 'N_MatchInTab', |
||
+ | 'UniqueMatch' |
||
+ | }, |
||
+ | where = ('OverviewPage="%s"'):format(page), |
||
+ | types = { |
||
+ | -- keep winner as a string since that's what's expected from args sigh |
||
+ | Team1Score = 'number', |
||
+ | Team2Score = 'number', |
||
+ | FF = 'number' |
||
+ | }, |
||
+ | limit = 9999 |
||
+ | } |
||
+ | return util_cargo.queryAndCast(query) |
||
+ | end |
||
+ | |||
+ | function h.processCargoData(data) |
||
+ | local processed = {} |
||
+ | for _, row in ipairs(data) do |
||
+ | h.sortFF(row) |
||
+ | processed[('%s_%s'):format(row.Tab,row.N_MatchInTab)] = { |
||
+ | winner = row.Winner, |
||
+ | team1 = { score = row.Team1Score, team = row.Team1, teamfinal = row.Team1Final }, |
||
+ | team2 = { score = row.Team2Score, team = row.Team2, teamfinal = row.Team2Final }, |
||
+ | } |
||
+ | end |
||
+ | return processed |
||
+ | end |
||
+ | |||
+ | function h.sortFF(row) |
||
+ | if row.FF == 1 then |
||
+ | row.Team1Score = 'FF' |
||
+ | row.Team2Score = 'W' |
||
+ | elseif row.FF == 2 then |
||
+ | row.Team1Score = 'W' |
||
+ | row.Team2Score = 'FF' |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function h.addProcessedToArgs(args, settings, processed, overviewPage) |
||
+ | for r, col in ipairs(settings) do |
||
+ | h.initializeMatch(args, r) |
||
+ | local title = args[r] and args[r].title or col.matches.title or '' |
||
+ | for m, _ in ipairs(col.matches) do |
||
+ | h.initializeMatch(args, r, m) |
||
+ | local argmatch = args[r] and args[r][m] |
||
+ | if argmatch and argmatch.cargomatch then |
||
+ | h.addMatchCargoToMatch(argmatch, processed[argmatch.cargomatch]) |
||
+ | else |
||
+ | -- the uniquematch does NOT include page number in it |
||
+ | local uniquematch = ('%s_%s'):format(title, m) |
||
+ | if not argmatch then |
||
+ | h.initializeMatch(args, r, m) |
||
+ | argmatch = args[r][m] |
||
+ | end |
||
+ | h.addMatchCargoToMatch(argmatch, processed[uniquematch]) |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function h.addMatchCargoToMatch(argMatch, cargoDataMatch) |
||
+ | if not cargoDataMatch then |
||
+ | return |
||
+ | end |
||
+ | -- allow arg data to overwrite cargo data always if applicable |
||
+ | argMatch.winner = argMatch.winner or cargoDataMatch.winner |
||
+ | for _, team in ipairs({ 'team1', 'team2' }) do |
||
+ | for k, v in pairs(cargoDataMatch[team]) do |
||
+ | argMatch[team][k] = argMatch[team][k] or v |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | -- print |
||
+ | function h.makeOutput(args, settings) |
||
+ | local output = mw.html.create() |
||
+ | if settings.togglers then |
||
+ | h.printAllBrackets(args, settings, output) |
||
+ | else |
||
+ | h.printBracket(args, settings, output:tag('div'), {}) |
||
+ | end |
||
+ | return output |
||
+ | end |
||
+ | |||
+ | function h.printAllBrackets(args, settings, output) |
||
+ | local toggleN = util_vars.setGlobalIndex('BracketToggler') |
||
+ | local togglers = h.makeTogglerButtons(settings.togglers, toggleN) |
||
+ | local tblRound1 = h.printNextBracketDiv(output, toggleN, 1) |
||
+ | h.printBracket(args, settings, tblRound1, togglers) |
||
+ | local tableList = { tblRound1 } |
||
+ | for i, toggle in ipairs(settings.togglers) do |
||
+ | h.setupNextToggle(settings, args, togglers, toggle, i) |
||
+ | local tbl = h.printNextBracketDiv(output, toggleN, i + 1) |
||
+ | h.printBracket(args, toggle.bracket, tbl, togglers) |
||
+ | tableList[#tableList+1] = tbl |
||
+ | end |
||
+ | h.setTableHidden(tableList, args.initround) |
||
+ | end |
||
+ | |||
+ | function h.setupNextToggle(settings, args, togglers, toggle, i) |
||
+ | h.fixColumnLabelsForToggle(settings, toggle.bracket, i) |
||
+ | table.remove(args, 1) |
||
+ | table.remove(togglers, 1) |
||
+ | h.processSettings(toggle.bracket, args) |
||
+ | end |
||
+ | |||
+ | function h.fixColumnLabelsForToggle(settings, bracket, i) |
||
+ | for k, col in ipairs(bracket) do |
||
+ | col.matches.title = settings[k + i].matches.title |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function h.printNextBracketDiv(output, toggleN, i) |
||
+ | local div = output:tag('div') |
||
+ | :addClass(h.allToggleClass(toggleN, false)) |
||
+ | :addClass(h.roundToggleClass(toggleN, i, false)) |
||
+ | return div |
||
+ | end |
||
+ | |||
+ | function h.allToggleClass(n, isAttr) |
||
+ | local dot = isAttr and '.' or '' |
||
+ | return ('%sbracket-toggle-allrounds-%s'):format(dot, n) |
||
+ | end |
||
+ | |||
+ | function h.roundToggleClass(n, i, isAttr) |
||
+ | local dot = isAttr and '.' or '' |
||
+ | return ('%sbracket-toggle-round-%s-%s'):format(dot, n, i) |
||
+ | end |
||
+ | |||
+ | function h.makeTogglerButtons(togglers, n) |
||
+ | local tbl = {} |
||
+ | tbl[1] = h.makeToggler(n, 1) |
||
+ | for i, _ in ipairs(togglers) do |
||
+ | if i == #togglers then |
||
+ | tbl[#tbl+1] = h.makeLastToggler(n) |
||
+ | else |
||
+ | -- first add 1 because we already did 1 from the default bracket |
||
+ | tbl[#tbl+1] = h.makeToggler(n, i + 1) |
||
+ | end |
||
+ | end |
||
+ | return tbl |
||
+ | end |
||
+ | |||
+ | function h.makeToggler(n, i) |
||
+ | local div = mw.html.create('div') |
||
+ | :addClass('bracket-toggler') |
||
+ | :wikitext('[') |
||
+ | div:tag('span') |
||
+ | :addClass('alwaysactive-toggler') |
||
+ | :attr('data-toggler-hide', h.allToggleClass(n, true)) |
||
+ | :attr('data-toggler-show', h.roundToggleClass(n, i + 1, true)) |
||
+ | :wikitext('x') |
||
+ | div:wikitext(']') |
||
+ | return div |
||
+ | end |
||
+ | |||
+ | function h.makeLastToggler(n) |
||
+ | local div = mw.html.create('div') |
||
+ | :addClass('bracket-toggler') |
||
+ | div:tag('span') |
||
+ | :addClass('alwaysactive-toggler') |
||
+ | :attr('data-toggler-hide', h.allToggleClass(n, true)) |
||
+ | :attr('data-toggler-show', h.roundToggleClass(n, 1, true)) |
||
+ | :wikitext('<<') |
||
+ | return div |
||
+ | end |
||
+ | |||
+ | function h.setTableHidden(tableList, initround) |
||
+ | initround = tonumber(initround or 1) or 1 |
||
+ | for k, tbl in ipairs(tableList) do |
||
+ | if k ~= initround then |
||
+ | tbl:addClass('toggle-section-hidden') |
||
+ | end |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function h.printBracket(args, settings, tbl, togglers) |
||
+ | tbl:addClass('bracket-grid') |
||
:css({ |
:css({ |
||
− | ['grid-template-columns'] = h. |
+ | ['grid-template-columns'] = h.getGTC(settings, args), |
+ | ['grid-template-rows'] = h.getGTR(settings, args.notitle) |
||
}) |
}) |
||
for round, col in ipairs(settings) do |
for round, col in ipairs(settings) do |
||
h.addLinesColumn(tbl, col.lines, round, not args.notitle) |
h.addLinesColumn(tbl, col.lines, round, not args.notitle) |
||
− | h.addMatchesColumn(tbl, args, col.matches, round, not args.notitle) |
+ | h.addMatchesColumn(tbl, args, col.matches, round, not args.notitle, togglers[round]) |
end |
end |
||
return tbl |
return tbl |
||
end |
end |
||
− | function h. |
+ | function h.getGTC(settings, args) |
− | local |
+ | local scores = {} |
− | + | for round, col in ipairs(settings) do |
|
+ | scores[round] = args[round] and tonumber(args[round].extendedseries or '') or col.extendedseries or 1 |
||
− | return ('%s repeat(%s, %s 3em)'):format( |
||
+ | end |
||
− | firstCol and '3em' or '0', |
||
+ | local firstcol = settings[1].lines and next(settings[1].lines) |
||
− | (len * 2 - 1), |
||
+ | local firstwidth = firstcol and LINEWIDTH or '0' |
||
− | roundwidth or '16em' |
||
+ | return h.getCustomGTC(scores, args.roundwidth, args.roundminwidth, firstwidth) |
||
− | ) |
||
+ | end |
||
+ | |||
+ | function h.getCustomGTC(scores, roundwidth, minwidth, firstwidth) |
||
+ | local linewidth = minwidth and ' minmax(2em,3em) ' or ' 3em ' |
||
+ | roundwidth = h.getRoundwidth(roundwidth) |
||
+ | minwidth = h.parseWidth(minwidth) or roundwidth |
||
+ | local widths = {} |
||
+ | for k, v in ipairs(scores) do |
||
+ | local min = (SCOREWIDTH * (v - 1) + minwidth) |
||
+ | local max = (SCOREWIDTH * (v - 1) + roundwidth) |
||
+ | widths[#widths+1] = ('minmax(%sem, %sem)'):format(min, max) |
||
+ | end |
||
+ | return firstwidth .. ' ' .. table.concat(widths, linewidth) |
||
+ | end |
||
+ | |||
+ | function h.getRoundwidth(roundwidth) |
||
+ | if roundwidth then |
||
+ | return h.parseWidth(roundwidth) |
||
+ | else |
||
+ | return ROUNDWIDTH |
||
+ | end |
||
+ | end |
||
+ | |||
+ | function h.parseWidth(width) |
||
+ | if not width then return nil end |
||
+ | return tonumber(width:gsub('em','') or '') |
||
+ | end |
||
+ | |||
+ | function h.getGTR(settings, notitle) |
||
+ | local max = 0 |
||
+ | for _, col in ipairs(settings) do |
||
+ | local total = 0 |
||
+ | for _, match in ipairs(col.matches) do |
||
+ | total = total + (match.above or 0) |
||
+ | if match.display == 'match' then |
||
+ | total = total + ROWS_PER_TEAM |
||
+ | elseif match.display == 'hline' then |
||
+ | total = total + ROWS_PER_HLINE |
||
+ | end |
||
+ | end |
||
+ | if total > max then |
||
+ | max = total |
||
+ | end |
||
+ | end |
||
+ | if not notitle then max = max + ROWS_PER_TITLE end |
||
+ | return ('repeat(%s,var(--grid-row-height))'):format(max) |
||
end |
end |
||
Line 67: | Line 377: | ||
function h.addBracketLine(tbl, roundname, linerow, extra) |
function h.addBracketLine(tbl, roundname, linerow, extra) |
||
+ | if linerow.above + extra > 0 then |
||
− | tbl:tag('div') |
||
− | : |
+ | tbl:tag('div') |
− | :addClass( |
+ | :addClass('bracket-line') |
+ | :addClass(roundname) |
||
− | :cssText(('grid-row:span %s;'):format(linerow.above + extra)) |
||
+ | :cssText(('grid-row:span %s;'):format(linerow.above + extra)) |
||
+ | end |
||
tbl:tag('div') |
tbl:tag('div') |
||
:addClass('bracket-line') |
:addClass('bracket-line') |
||
Line 79: | Line 391: | ||
end |
end |
||
− | function h.addMatchesColumn(tbl, args, data, r, addtitle) |
+ | function h.addMatchesColumn(tbl, args, data, r, addtitle, toggler) |
local roundname = 'round' .. r |
local roundname = 'round' .. r |
||
if addtitle then |
if addtitle then |
||
− | + | local title = args[r] and args[r].title or data.title or '' |
|
+ | h.makeTitle(tbl, roundname, title, toggler) |
||
end |
end |
||
for m, row in ipairs(data) do |
for m, row in ipairs(data) do |
||
− | local game = args[ |
+ | local game = args[r] and args[r][m] or { team1 = {}, team2 = {} } |
if row.above then |
if row.above then |
||
h.addSpacer(tbl, roundname, row.above) |
h.addSpacer(tbl, roundname, row.above) |
||
end |
end |
||
if row.display == 'match' then |
if row.display == 'match' then |
||
− | h.makeMatch(tbl, game, roundname, row.label) |
+ | h.makeMatch(tbl, game, roundname, not args.nolabels and row.label, args.teamstyle) |
elseif row.display == 'hline' then |
elseif row.display == 'hline' then |
||
h.makeHorizontalCell(tbl, roundname) |
h.makeHorizontalCell(tbl, roundname) |
||
Line 98: | Line 411: | ||
end |
end |
||
− | function h.makeTitle(tbl, roundname, text) |
+ | function h.makeTitle(tbl, roundname, text, toggler) |
− | tbl:tag('div') |
+ | local outerdiv = tbl:tag('div') |
:addClass('bracket-grid-header') |
:addClass('bracket-grid-header') |
||
:addClass(roundname) |
:addClass(roundname) |
||
− | + | local innerdiv = outerdiv:tag('div') |
|
− | + | :addClass('bracket-header-content') |
|
− | + | :wikitext(text) |
|
+ | if toggler then |
||
− | return |
||
+ | innerdiv:node(toggler) |
||
+ | end |
||
end |
end |
||
function h.makeHorizontalCell(tbl, roundname) |
function h.makeHorizontalCell(tbl, roundname) |
||
− | h.addSpacer(tbl, roundname, 2) |
||
tbl:tag('div') |
tbl:tag('div') |
||
:addClass('bracket-spacer') |
:addClass('bracket-spacer') |
||
:addClass('horizontal') |
:addClass('horizontal') |
||
:addClass(roundname) |
:addClass(roundname) |
||
− | :cssText('grid-row:span 2;') |
||
return |
return |
||
end |
end |
||
− | function h.makeMatch(tbl, game, roundname, label) |
+ | function h.makeMatch(tbl, game, roundname, label, teamstyle) |
if game.label then label = game.label end |
if game.label then label = game.label end |
||
h.addSpacer(tbl, roundname, nil, label) |
h.addSpacer(tbl, roundname, nil, label) |
||
− | h.makeTeam(tbl, roundname, game |
+ | h.makeTeam(tbl, roundname, game, game.team1, '1', teamstyle) |
− | h.makeTeam(tbl, roundname, game |
+ | h.makeTeam(tbl, roundname, game, game.team2, '2', teamstyle) |
h.addSpacer(tbl, roundname) |
h.addSpacer(tbl, roundname) |
||
return |
return |
||
Line 131: | Line 444: | ||
:addClass('bracket-spacer') |
:addClass('bracket-spacer') |
||
:addClass(roundname) |
:addClass(roundname) |
||
− | + | if label then |
|
+ | div:wikitext(label) |
||
+ | end |
||
if n then |
if n then |
||
div:cssText(('grid-row:span %s;'):format(n)) |
div:cssText(('grid-row:span %s;'):format(n)) |
||
Line 138: | Line 453: | ||
end |
end |
||
− | function h.makeTeam(tbl, roundname, data, |
+ | function h.makeTeam(tbl, roundname, game, data, n, teamstyle) |
+ | local isWinner = game.winner == n |
||
+ | local isbye = util_args.castAsBool(data.bye) |
||
local line = tbl:tag('div') |
local line = tbl:tag('div') |
||
:addClass('bracket-team') |
:addClass('bracket-team') |
||
:addClass(roundname) |
:addClass(roundname) |
||
+ | :addClass(game.class) |
||
+ | util_esports.addTeamHighlighter(line, data.teamfinal or data.playerlink or data.player or data.team) |
||
if isWinner then |
if isWinner then |
||
line:addClass('bracket-winner') |
line:addClass('bracket-winner') |
||
Line 149: | Line 468: | ||
if data.free then |
if data.free then |
||
team:wikitext(data.free) |
team:wikitext(data.free) |
||
+ | elseif isbye then |
||
+ | team:wikitext('BYE') |
||
+ | line:addClass('bracket-bye') |
||
else |
else |
||
− | bracket_wiki.teamDisplay(team, data) |
+ | bracket_wiki.teamDisplay(team, data, teamstyle) |
+ | end |
||
+ | h.makeScore(line, data.score, isbye, game.winners, n) |
||
+ | end |
||
+ | |||
+ | function h.makeScore(line, score, isbye, winners, n) |
||
+ | local tbl = util_text.split(tostring(score or ''),sep) |
||
+ | tbl_win = winners and util_text.split(winners, sep) or {} |
||
+ | for k, v in ipairs(tbl) do |
||
+ | local div = line:tag('div') |
||
+ | :addClass('bracket-team-points') |
||
+ | :wikitext(v or (isbye and '-') or '') |
||
+ | if tbl_win[k] == n then |
||
+ | div:addClass('bracket-score-winner') |
||
+ | elseif tbl_win[k] then |
||
+ | div:addClass('bracket-score-loser') |
||
+ | end |
||
end |
end |
||
− | line:tag('div') |
||
− | :addClass('bracket-team-points') |
||
− | :wikitext(data.score or '') |
||
− | return |
||
end |
end |
||
Line 163: | Line 497: | ||
local tpl_args = util_args.merge(true) |
local tpl_args = util_args.merge(true) |
||
-- use require instead of loadData so that we can use next() and # |
-- use require instead of loadData so that we can use next() and # |
||
− | local settings |
+ | local settings |
+ | local function assignBracket() |
||
+ | settings = require('Module:Bracket/'.. tpl_args[1]) |
||
+ | end |
||
+ | |||
+ | if not tpl_args[1] then |
||
+ | error('No bracket definition provided!') |
||
+ | elseif pcall(assignBracket) then |
||
+ | -- pass |
||
+ | else |
||
+ | error(('Bracket %s is not a valid input'):format(tpl_args[1])) |
||
+ | end |
||
+ | |||
local args = h.processArgs(tpl_args) |
local args = h.processArgs(tpl_args) |
||
+ | h.processSettings(settings, args) |
||
− | return h.printBracket(args, settings) |
||
+ | if util_args.castAsBool(args.cargo) then |
||
+ | h.addCargoData(args, settings) |
||
+ | end |
||
+ | return h.makeOutput(args, settings) |
||
end |
end |
||
Latest revision as of 22:39, 14 April 2019
To edit the documentation or categories for this module, click here.
local util_args = require('Module:ArgsUtil')
local util_cargo = require('Module:CargoUtil')
local util_esports = require('Module:EsportsUtil')
local util_table = require('Module:TableUtil')
local util_text = require('Module:TextUtil')
local util_vars = require('Module:VarsUtil')
local bracket_wiki = require('Module:Bracket/Wiki') -- wiki localization per game
local m_team = require('Module:Team')
local lang = mw.getLanguage('en')
local ROWS_PER_TEAM = 6
local ROWS_PER_TITLE = 2
local ROWS_PER_HLINE = 1
local ROUNDWIDTH = 12
local LINEWIDTH = '3em'
local SCOREWIDTH = 2
local sep = '%s*,%s*'
local h = {}
function h.processArgs(tpl_args)
-- format tpl_args
local args = {}
for k, v in pairs(tpl_args) do
if type(k) ~= 'string' then
-- pass
elseif k:find('R%d+M%d+_.*_') then
local r, m, val, team = k:match('R(%d+)M(%d+)_(.*)_(%d+)')
r = tonumber(r)
m = tonumber(m)
h.initializeMatch(args, r, m)
if val == 'team' then
args[r][m]['team' .. team][val] = m_team.teamlinkname(v)
else
args[r][m]['team' .. team][val] = v
end
elseif k:find('R%d+M%d+_.*') then
local r, m, val = k:match('R(%d+)M(%d+)_(.*)')
r = tonumber(r)
m = tonumber(m)
h.initializeMatch(args, r, m)
args[r][m][val] = v
elseif k:find('R%d+_') then
local r, val = k:match('R(%d+)_(.*)')
r = tonumber(r)
h.initializeMatch(args, r)
args[r][val] = v
else
args[k] = v
end
end
return args
end
function h.initializeMatch(args, r, m)
if not args[r] then
args[r] = {}
end
if not args[r][m] and m then
args[r][m] = { team1 = {}, team2 = {} }
end
end
function h.processSettings(settings, args)
-- in theory this could be done in the settings module before returning but
-- this way the code is a bit more hidden from users editing stuff
-- and also this makes the settings module closer to a read-only table that you
-- import (and clone) here which i guess is nice?
-- tbh im not sure if this was the right way to do it tho
for r, col in ipairs(settings) do
local m = #col.matches
while m >= 1 do
-- need to iterate backwards bc we'll delete third-place matches if hidden
local match = col.matches[m]
local lines = col.lines and col.lines[m]
if lines and lines.reseed then
lines.class = lines.class:format(lang:lc(args.reseed or 'reseeding'))
end
if match.argtoshow then
if not util_args.castAsBool(args[match.argtoshow]) then
if col.matches[m+1] then
col.matches[m+1].above = (col.matches[m+1].above or 0) + (match.above or 0) + 6
end
table.remove(col.matches,m)
end
end
m = m - 1
end
end
end
-- cargo
function h.addCargoData(args, settings)
local overviewPage = util_esports.getOverviewPage(args.page)
local data = h.doCargoQuery(overviewPage)
if not next(data) then
return
end
local processed = h.processCargoData(data)
h.addProcessedToArgs(args, settings, processed, overviewPage)
end
function h.doCargoQuery(page)
local query = {
tables = 'MatchSchedule',
fields = {
'Team1',
'Team2',
'Team1Final',
'Team2Final',
'Winner',
'FF',
'Team1Score',
'Team2Score',
'Tab',
'N_MatchInTab',
'UniqueMatch'
},
where = ('OverviewPage="%s"'):format(page),
types = {
-- keep winner as a string since that's what's expected from args sigh
Team1Score = 'number',
Team2Score = 'number',
FF = 'number'
},
limit = 9999
}
return util_cargo.queryAndCast(query)
end
function h.processCargoData(data)
local processed = {}
for _, row in ipairs(data) do
h.sortFF(row)
processed[('%s_%s'):format(row.Tab,row.N_MatchInTab)] = {
winner = row.Winner,
team1 = { score = row.Team1Score, team = row.Team1, teamfinal = row.Team1Final },
team2 = { score = row.Team2Score, team = row.Team2, teamfinal = row.Team2Final },
}
end
return processed
end
function h.sortFF(row)
if row.FF == 1 then
row.Team1Score = 'FF'
row.Team2Score = 'W'
elseif row.FF == 2 then
row.Team1Score = 'W'
row.Team2Score = 'FF'
end
end
function h.addProcessedToArgs(args, settings, processed, overviewPage)
for r, col in ipairs(settings) do
h.initializeMatch(args, r)
local title = args[r] and args[r].title or col.matches.title or ''
for m, _ in ipairs(col.matches) do
h.initializeMatch(args, r, m)
local argmatch = args[r] and args[r][m]
if argmatch and argmatch.cargomatch then
h.addMatchCargoToMatch(argmatch, processed[argmatch.cargomatch])
else
-- the uniquematch does NOT include page number in it
local uniquematch = ('%s_%s'):format(title, m)
if not argmatch then
h.initializeMatch(args, r, m)
argmatch = args[r][m]
end
h.addMatchCargoToMatch(argmatch, processed[uniquematch])
end
end
end
end
function h.addMatchCargoToMatch(argMatch, cargoDataMatch)
if not cargoDataMatch then
return
end
-- allow arg data to overwrite cargo data always if applicable
argMatch.winner = argMatch.winner or cargoDataMatch.winner
for _, team in ipairs({ 'team1', 'team2' }) do
for k, v in pairs(cargoDataMatch[team]) do
argMatch[team][k] = argMatch[team][k] or v
end
end
end
-- print
function h.makeOutput(args, settings)
local output = mw.html.create()
if settings.togglers then
h.printAllBrackets(args, settings, output)
else
h.printBracket(args, settings, output:tag('div'), {})
end
return output
end
function h.printAllBrackets(args, settings, output)
local toggleN = util_vars.setGlobalIndex('BracketToggler')
local togglers = h.makeTogglerButtons(settings.togglers, toggleN)
local tblRound1 = h.printNextBracketDiv(output, toggleN, 1)
h.printBracket(args, settings, tblRound1, togglers)
local tableList = { tblRound1 }
for i, toggle in ipairs(settings.togglers) do
h.setupNextToggle(settings, args, togglers, toggle, i)
local tbl = h.printNextBracketDiv(output, toggleN, i + 1)
h.printBracket(args, toggle.bracket, tbl, togglers)
tableList[#tableList+1] = tbl
end
h.setTableHidden(tableList, args.initround)
end
function h.setupNextToggle(settings, args, togglers, toggle, i)
h.fixColumnLabelsForToggle(settings, toggle.bracket, i)
table.remove(args, 1)
table.remove(togglers, 1)
h.processSettings(toggle.bracket, args)
end
function h.fixColumnLabelsForToggle(settings, bracket, i)
for k, col in ipairs(bracket) do
col.matches.title = settings[k + i].matches.title
end
end
function h.printNextBracketDiv(output, toggleN, i)
local div = output:tag('div')
:addClass(h.allToggleClass(toggleN, false))
:addClass(h.roundToggleClass(toggleN, i, false))
return div
end
function h.allToggleClass(n, isAttr)
local dot = isAttr and '.' or ''
return ('%sbracket-toggle-allrounds-%s'):format(dot, n)
end
function h.roundToggleClass(n, i, isAttr)
local dot = isAttr and '.' or ''
return ('%sbracket-toggle-round-%s-%s'):format(dot, n, i)
end
function h.makeTogglerButtons(togglers, n)
local tbl = {}
tbl[1] = h.makeToggler(n, 1)
for i, _ in ipairs(togglers) do
if i == #togglers then
tbl[#tbl+1] = h.makeLastToggler(n)
else
-- first add 1 because we already did 1 from the default bracket
tbl[#tbl+1] = h.makeToggler(n, i + 1)
end
end
return tbl
end
function h.makeToggler(n, i)
local div = mw.html.create('div')
:addClass('bracket-toggler')
:wikitext('[')
div:tag('span')
:addClass('alwaysactive-toggler')
:attr('data-toggler-hide', h.allToggleClass(n, true))
:attr('data-toggler-show', h.roundToggleClass(n, i + 1, true))
:wikitext('x')
div:wikitext(']')
return div
end
function h.makeLastToggler(n)
local div = mw.html.create('div')
:addClass('bracket-toggler')
div:tag('span')
:addClass('alwaysactive-toggler')
:attr('data-toggler-hide', h.allToggleClass(n, true))
:attr('data-toggler-show', h.roundToggleClass(n, 1, true))
:wikitext('<<')
return div
end
function h.setTableHidden(tableList, initround)
initround = tonumber(initround or 1) or 1
for k, tbl in ipairs(tableList) do
if k ~= initround then
tbl:addClass('toggle-section-hidden')
end
end
end
function h.printBracket(args, settings, tbl, togglers)
tbl:addClass('bracket-grid')
:css({
['grid-template-columns'] = h.getGTC(settings, args),
['grid-template-rows'] = h.getGTR(settings, args.notitle)
})
for round, col in ipairs(settings) do
h.addLinesColumn(tbl, col.lines, round, not args.notitle)
h.addMatchesColumn(tbl, args, col.matches, round, not args.notitle, togglers[round])
end
return tbl
end
function h.getGTC(settings, args)
local scores = {}
for round, col in ipairs(settings) do
scores[round] = args[round] and tonumber(args[round].extendedseries or '') or col.extendedseries or 1
end
local firstcol = settings[1].lines and next(settings[1].lines)
local firstwidth = firstcol and LINEWIDTH or '0'
return h.getCustomGTC(scores, args.roundwidth, args.roundminwidth, firstwidth)
end
function h.getCustomGTC(scores, roundwidth, minwidth, firstwidth)
local linewidth = minwidth and ' minmax(2em,3em) ' or ' 3em '
roundwidth = h.getRoundwidth(roundwidth)
minwidth = h.parseWidth(minwidth) or roundwidth
local widths = {}
for k, v in ipairs(scores) do
local min = (SCOREWIDTH * (v - 1) + minwidth)
local max = (SCOREWIDTH * (v - 1) + roundwidth)
widths[#widths+1] = ('minmax(%sem, %sem)'):format(min, max)
end
return firstwidth .. ' ' .. table.concat(widths, linewidth)
end
function h.getRoundwidth(roundwidth)
if roundwidth then
return h.parseWidth(roundwidth)
else
return ROUNDWIDTH
end
end
function h.parseWidth(width)
if not width then return nil end
return tonumber(width:gsub('em','') or '')
end
function h.getGTR(settings, notitle)
local max = 0
for _, col in ipairs(settings) do
local total = 0
for _, match in ipairs(col.matches) do
total = total + (match.above or 0)
if match.display == 'match' then
total = total + ROWS_PER_TEAM
elseif match.display == 'hline' then
total = total + ROWS_PER_HLINE
end
end
if total > max then
max = total
end
end
if not notitle then max = max + ROWS_PER_TITLE end
return ('repeat(%s,var(--grid-row-height))'):format(max)
end
function h.addLinesColumn(tbl, lineData, r, addtitle)
local roundname = 'round' .. (r - 1)
if not lineData then
return
end
for m, row in ipairs(lineData) do
if m == 1 and addtitle then
h.addBracketLine(tbl, roundname, row, 2)
else
h.addBracketLine(tbl, roundname, row, 0)
end
end
return
end
function h.addBracketLine(tbl, roundname, linerow, extra)
if linerow.above + extra > 0 then
tbl:tag('div')
:addClass('bracket-line')
:addClass(roundname)
:cssText(('grid-row:span %s;'):format(linerow.above + extra))
end
tbl:tag('div')
:addClass('bracket-line')
:addClass(linerow.class)
:addClass(roundname)
:cssText(('grid-row:span %s;'):format(linerow.height))
return
end
function h.addMatchesColumn(tbl, args, data, r, addtitle, toggler)
local roundname = 'round' .. r
if addtitle then
local title = args[r] and args[r].title or data.title or ''
h.makeTitle(tbl, roundname, title, toggler)
end
for m, row in ipairs(data) do
local game = args[r] and args[r][m] or { team1 = {}, team2 = {} }
if row.above then
h.addSpacer(tbl, roundname, row.above)
end
if row.display == 'match' then
h.makeMatch(tbl, game, roundname, not args.nolabels and row.label, args.teamstyle)
elseif row.display == 'hline' then
h.makeHorizontalCell(tbl, roundname)
end
end
return
end
function h.makeTitle(tbl, roundname, text, toggler)
local outerdiv = tbl:tag('div')
:addClass('bracket-grid-header')
:addClass(roundname)
local innerdiv = outerdiv:tag('div')
:addClass('bracket-header-content')
:wikitext(text)
if toggler then
innerdiv:node(toggler)
end
end
function h.makeHorizontalCell(tbl, roundname)
tbl:tag('div')
:addClass('bracket-spacer')
:addClass('horizontal')
:addClass(roundname)
return
end
function h.makeMatch(tbl, game, roundname, label, teamstyle)
if game.label then label = game.label end
h.addSpacer(tbl, roundname, nil, label)
h.makeTeam(tbl, roundname, game, game.team1, '1', teamstyle)
h.makeTeam(tbl, roundname, game, game.team2, '2', teamstyle)
h.addSpacer(tbl, roundname)
return
end
function h.addSpacer(tbl, roundname, n, label)
local div = tbl:tag('div')
:addClass('bracket-spacer')
:addClass(roundname)
if label then
div:wikitext(label)
end
if n then
div:cssText(('grid-row:span %s;'):format(n))
end
return
end
function h.makeTeam(tbl, roundname, game, data, n, teamstyle)
local isWinner = game.winner == n
local isbye = util_args.castAsBool(data.bye)
local line = tbl:tag('div')
:addClass('bracket-team')
:addClass(roundname)
:addClass(game.class)
util_esports.addTeamHighlighter(line, data.teamfinal or data.playerlink or data.player or data.team)
if isWinner then
line:addClass('bracket-winner')
end
local team = line:tag('div')
:addClass('bracket-team-name')
if data.free then
team:wikitext(data.free)
elseif isbye then
team:wikitext('BYE')
line:addClass('bracket-bye')
else
bracket_wiki.teamDisplay(team, data, teamstyle)
end
h.makeScore(line, data.score, isbye, game.winners, n)
end
function h.makeScore(line, score, isbye, winners, n)
local tbl = util_text.split(tostring(score or ''),sep)
tbl_win = winners and util_text.split(winners, sep) or {}
for k, v in ipairs(tbl) do
local div = line:tag('div')
:addClass('bracket-team-points')
:wikitext(v or (isbye and '-') or '')
if tbl_win[k] == n then
div:addClass('bracket-score-winner')
elseif tbl_win[k] then
div:addClass('bracket-score-loser')
end
end
end
local p = {}
function p.main(frame)
local tpl_args = util_args.merge(true)
-- use require instead of loadData so that we can use next() and #
local settings
local function assignBracket()
settings = require('Module:Bracket/'.. tpl_args[1])
end
if not tpl_args[1] then
error('No bracket definition provided!')
elseif pcall(assignBracket) then
-- pass
else
error(('Bracket %s is not a valid input'):format(tpl_args[1]))
end
local args = h.processArgs(tpl_args)
h.processSettings(settings, args)
if util_args.castAsBool(args.cargo) then
h.addCargoData(args, settings)
end
return h.makeOutput(args, settings)
end
return p