Module:RoundN: Difference between revisions

Content deleted Content added
mNo edit summary
per edit request on talk page - fix the node_function{orphan} bug
 
(53 intermediate revisions by 14 users not shown)
Line 1:
local p = {
RD = {
'Quarter Finals-finals',
'Semi Finals-finals',
'Final',
'Third Placeplace'
},
-- The text and background colors are paired and when you set one, you should set the other (accessibility)
textColor = {head = '#202122', '#202122', '#202122', '#202122', '#202122'},
bgColor = {head = '#f2f2f2', 'gold', 'silver', '#C96', '#f9f9f9'},
reuseStr = {},
saveStr = function(self, name, ...)
--always call this like so: (p.reuseStr.name or p:saveStr('name', 'string' .. 'string' .. etc))
if not self.reuseStr[name] then
--this avoids repeating the concatenation operation with each loop
self.reuseStr[name] = table.concat{...}
saveStr = function(self, name, concatString)
end
self.reuseStr[name] = concatString
return concatStringself.reuseStr[name]
end,
cleaned = {}
}
 
--Provides a convenient naming shortcut up to {{#invoke:RoundN|N512}} = {{invoke:RoundN|main|columns = 9}}
local rowNum, head = {}, {}
for columns = 1, 9 do
local tab = mw.html.create'table'
local N = math.pow(2, columns)
p['N' .. N] = function(frame)
return p.main(frame.args, columns)
end
p['n' .. N] = p['N' .. N]--to make case insensitive
end
 
--saves memory and avoids errors when using a nil as a table by providing a temporary table; if using nil as false; use 'table(k)' to look up table[k]
p.nilAsTab = {
__index = function(t, i)
return setmetatable({}, setmetatable(p.nilAsTab, {__index = {t = t, i = i}}))
end,
__newindex = function (pt, pi, v) --store new values in actual table rather than temporary
rawset(p.nilAsTab.t, p.nilAsTab.i, {})[p.nilAsTab.i][pi] = v
setmetatable(p.nilAsTab.t[p.nilAsTab.i], {__call = p.nilAsTab.__call})
end,
__call = function(t, i)
return t and rawget(t, i)
end
}
--never assign a value to these or they will stop being empty
local infiniteEmpty = setmetatable({}, {__index = setmetatable({}, p.nilAsTab), p.nilAsTab}) -- infiniteEmpty[1][2][3]...[infinity] = {}
local callableEmpty = setmetatable({}, p.nilAsTab)
 
local rowNum, head, m, col, tab, esc = {}, {}, {num = 1, phase = 0, bold = infiniteEmpty}, {}, mw.html.create'table', {
bs = require'Module:Escape',--backslash
comma = {['(%([^,]*),([^%)]*%))'] = '%1|@!#|%2'},--escape commas in ()
}
local nodeFunc = {
scanPattern = function(self, args, step)
esc = {
bs = require'Module:Escape',--backslash
comma = {['(%([^,]*),([^%)]*%))'] = '%1|@!#|%2'},--escape commas in ()
},
scanPattern = function(self, args, step, mNum)
self.pattern = nil
if args[step] then
self.pattern, self.nonFunc = string.match(self.esc.bs:text(args[step]), '^node_function{(.-)}(.*)')
end
if self.pattern then
for k, v in pairs(self.esc.comma) do
self.pattern = self.pattern:gsub(k, v)
end
self.nonFunc = self.nonFunc and self.esc.bs:undo(self.nonFunc)
self.pattern = mw.text.split(self.pattern, '%s*,%s*')
for k, v in ipairs(self.pattern) do
Line 40 ⟶ 65:
self.pattern[k] = func
if arg then
for x, y in pairs(self.esc.comma) do
arg = self.esc.bs:undo(arg):gsub(y:gsub('%%%d', ''), x:match('%)([^%(])%(') or x:gsub('\\', ''))
end
self[func].arg = self[func].arg or {}
self[func].arg[mNumm.num] = arg
end
end
Line 52 ⟶ 77:
end,
helper = {
freeCellstopBranch = function()--node is top of fork if top is 0
return (m.num - col.top) % 2
local v = p.getNodeFunc().helper.info
end,
local count = 0
addText = function(text)
if not v.occupied.full then
if text and text ~= '' then
for _, x in ipairs(v.occupied) do
tab.r:wikitext(text)
for _, y in ipairs(x) do
count = count + 1
end
end
v.occupied.full = v.totalCells == count
end
if v.occupied.full then
return false
end
return v.totalCells - count
end,
top = function()--node is top of fork if top is 0
return (p.getNodeFunc().helper.info.num - p.getNodeFunc().helper.info.first) % 2
end
},
line = {--this node is omitted and replaced with a line
main = function(x)
local nFh = p.getNodeFunc()
if m.available then
local h = nF.helper
local text, topId, isTop, notTop = h.line.arg[m.num] or '', h.topBranch()
if h.freeCells() == h.info.totalCells then
localisTop top= topId == h.top()0
notTop = {[isTop and 1 or 0] = p.reuseStr.solid}
for k = 0, 1 do
tab.r = rowNum[h.infom.rowr + k * 4]:tag'td'
:css(notTop[k] and
top == 0{[isTop and 'border-top' or 'border-bottom',] = notTop[k]}
k ~= top and p.reuseStr.solid or nil{}
)
:attr{
rowspan = k({[0] == 0 and 4 or, 2})[k],
colspan = p.colspan
}
h.addText(text or h.nonFunc)
:wikitext(k == 0 and nF.line.arg[h.info.num] or '')
text = nil
end
hm.info.occupied.fullavailable = truefalse
else
return nil
Line 99 ⟶ 115:
bridge = {--Draw a line to the neighboring node in the same column that is not connected to the current node
main = function(x)
local nFh = p.getNodeFunc()
h.bridge.lay[col.c][m.num - col.top + 1 + (h.topBranch() == 1 and 1 or -1)] = true
local v = nF.helper.info
h.addText(nonFunc)
nF.bridge.lay[v.col][v.num - v.first + 1 + (nF.helper.top() == 1 and 1 or -1)] = true
return x
end,
lay = setmetatable({}, p.nilAsTab)
},
canvas = {--Merges all cells in node. Content will be the next parameter.
main = function(x)
local helperh = p.getNodeFunc().helper
if helperm.freeCells() == helper.info.totalCellsavailable then
tab.r = rowNum[helperm.info.rowr]:tag'td'
:attr{
rowspan = 6,
colspan = p.colspan
}
h.addText(h.nonFunc)
helper.info.occupied.full = true
m.available = false
return x
else
Line 124 ⟶ 141:
orphan = {--sets a flag for skipMatch to be set by p._main
main = function(x)
local nF = p.getNodeFunc().orphan.num = m.num
nF.orphan.num = nF.helper.info.num
return x
end
Line 135 ⟶ 151:
}
 
setmetatable(nodeFunc.helper, {__index = nodeFunc})
function p.getNodeFunc()
return nodeFunc.helper
end
 
local function newRow(bodyRow)
--Provides a convenient naming shortcut up to {{#invoke:RoundN|N512}} = {{invoke:RoundN|main|columns = 9}}
local first = p.flex_tree.merge and mw.clone(p.flex_tree.cell) or p.flex_tree.cell
for c = 1, 9 do
local N = math.pow(2, c)
p['N' .. N] = function(frame)
return p.main(frame.args, c)
end
p['n' .. N] = p['N' .. N]--to make case insensitive
end
 
function newRow(bodyRow)
tab.r = tab:tag'tr'
:node(first)
tab.r:tag'td'
:css(p.flex_tree.css)
:wikitext(p.flex_tree.wt)
if bodyRow then
table.insert(rowNum, bodyRow, tab.r)
if p.flex_tree.merge then
rowNum[bodyRow].first = first
rowNum[bodyRow].first.unchanged = true
end
end
end
 
local function drawHead(text, row3rd)
local td = (row3rd and rowNum[row3rd]:tag'td':attr{rowspan = 2}
or head.row:tag'td')
:attr{colspan = p.colspan}
if text ~= 'omit_label' then
:css{
td:wikitext(text):css{
['text-align'] = 'center',
border = '1px solid #aaa',
['background-color'] = '#f2f2f2'p.bgColor.head,
color = p.textColor.head
}
end
:wikitext(text)
end
 
local function spacer(width)
tab.r:tag'td'
:attr{width = width}
Line 176 ⟶ 189:
end
 
local function dpBox(v, r)
p.dpBoxBase = p.dpBoxBase or mw.html.create'td':attr{rowspan = 2, colspan = p.colspan}
tab.r = rowNum[r]:tag'td'
if not v then
:wikitext(v or p.flex_tree.wt)
p.dpBoxEmpty = p.previewnumbers and mw.clone(p.dpBoxBase) or p.dpBoxEmpty or mw.clone(p.dpBoxBase):wikitext(p.flex_tree.wt)
:attr{rowspan = 2, colspan = p.colspan}
rowNum[r]:node(p.dpBoxEmpty)
else
rowNum[r]:node(mw.clone(p.dpBoxBase):wikitext(v))
end
end
 
p.scoreWasher = {
function cleanPts(s)
numberFormat = '%-?%d+%.?%d*',
return s and tonumber(s:gsub('<.+>', ''):gsub('%D', ''), 10) or s and math.huge or 0
main = function (self, s)
end
if s then
 
for _, cycle in ipairs(self.cycles) do
function cleanSum(clean)
s = s:gsub(unpack(cycle))
local sum = {0, 0}
end
for _, box in ipairs(clean) do
if p.scoreSumBox and self.plus then
for team, score in ipairs(box) do
local t = 0
sum[team] = sum[team] + score
for _, part in ipairs(mw.text.split(s, self.plus)) do
t = t + (tonumber(part:match('%-?%d+%.?%d*')) or 0)
end
return t
end
return tonumber(s:match(self.numberFormat)) or math.huge
end
return 0
end,
spin = function(self, v)
table.insert(self, v)
return self
end,
load = function (self, cycle)
local wash, rinse = 0, {spin = self.spin}
for v in cycle:gfind('%(([^%(%)]-)%)') do
if v == '_plus_' then
self.plus = v
rinse:spin(v)
cycle = cycle:gsub('%(_plus_%)', '', 1)
else
wash = wash + 1
rinse:spin('%'):spin(wash)
end
end
table.insert(self.cycles, {esc.bs:undo(cycle, '%%'), table.concat(rinse)})
end,
init = function(self, setting)
self.cycles = {original = setting}
for cycle in (setting and esc.bs:text(setting) or '{<.->} {[^%d]*}'):gfind('{(.-)}') do
self:load(cycle)
end
end,
sum = function (clean)
local sum = {0, 0}
for _, box in ipairs(clean) do
for team, score in ipairs(box) do
sum[team] = sum[team] + score
end
end
return unpack(math.max(unpack(sum)) == math.huge and {'&mdash;', '&mdash;'} or sum)
end
}
return unpack(sum)
end
 
local function boldWin(s1, s2)
return setmetatable(
return p.bold and s1 ~= s2 and (math[({'min', 'max'})[p.bold]](s1, s2) == s1 and {true} or {[2] = true}) or {}--({{s1 < s2, s1 > s2}, {s1 > s2, s1 < s2}, {}})[p.bold or 3]
p.bold and s1 ~= s2 and (math[({'min', 'max'})[p.bold]](s1, s2) == s1 and {true} or {[2] = true}) or callableEmpty,
p.nilAsTab
)
end
 
local function maxSpan(span, start, rows)
return math.min(span, math.max(0, rows - start + 1))
end
 
--in case of templates like RDseed need padding value
function teamBox(v, r, f)
p.teamBoxPadding = function()
tab.oldR = tab.r
return '.6ex'
tab.r = rowNum[r]:tag'td'
end
:css{
p.teamBoxPadTab = {padding = '0 ' .. p.teamBoxPadding()}
border = '1px solid #aaa',
p.teamBoxNormal = {border = '1px solid #aaa', ['background-color'] = p.bgColor[4], color = p.textColor[4]}
background = ({'gold', 'silver', '#C96', '#f9f9f9'})[f.color or 4],
local function teamBox(v, r, f)
[f[1] and 'text-align' or 'padding'] = f[1] or '0 .6ex'
if p.flex_tree.merge and not v and f.phase == 2 then
}
:attr{rowspanfor i = -2}, 0 do
if rowNum[r + i].first.unchanged then
tab.r:tag(f.bold and 'b' or ''):wikitext(v or '&nbsp;')
rowNum[r + i].first.unchanged = nil
rowNum[r + i].first:node(p.unflex_div)
end
end
tab.r:attr{rowspan = 4}:css{['vertical-align'] = 'center'}
else
if not p.bold then
--backwards compatability (wikitemplates bold each arg individually)
local hasBold, b = tostring(v):gsub("([^']*)'''([^']*)", '%1<b>%2</b>')
if b == 1 then
v = hasBold
end
end
local cell
if f[1] then
cell = f.sumBox and f.sumBox[1] and
{padding = f.sumBox[1]}
or {['border-left'] = f.borderLeft}
cell['text-align'] = v and f[1]
else
cell = p.teamBoxPadTab
end
local text = v or f[1] and '' or '&nbsp;'
if f.bold then
text = mw.ustring.gsub(text, '(%(%[%[[^%[%]]*%]%]%))', '<span style="font-weight:normal">%1</span>')
end
tab.r = rowNum[r]:tag'td'
:css(p.teamBoxCSS)
:css(cell)
:attr{rowspan = 2}
:node(mw.html.create(f.bold and 'b'):wikitext(text))
end
end
 
function p._main(args, frame)
function args:clean(key, params)--prevent html comments from breaking named args and reduces repeat concatenation
params = params or {}
Line 222 ⟶ 313:
if clean then
params.append = params.append or ''
clean = mw.text.decode(clean):gsub('<!%-.+-%->', ''):gsub(params.pattern or '[^%w-;%.]', '') .. params.append
clean = clean ~= params.append and clean or params.ifNil
end
Line 230 ⟶ 321:
p.cols = tonumber(args:clean('columns', {pattern = '%D'}))
p.tCols = (tonumber(args:clean('final_RDs_excluded', {pattern = '%D'})) or 0) + p.cols
p.bold = ({low = 1, high = 2})[args:clean('bold_winner')]
local matchPer = {
pattern = '%d*per%d+[%-x]%d+',
Line 239 ⟶ 329:
local mType, mNum = string.match(k, '^(%l+)match(%d*)$')
mType, mNum = ({skip = skipMatch, manualbold = unBold})[mType], tonumber(mNum)
if mType then
if mNum then
mType[mNum] = args:clean(k) == 'yes' or args[k] == 'true'
Line 262 ⟶ 352:
for _, v in ipairs({--more args to boolean
'widescore',
'template',
'color',
'color_repechage',
'3rdplace',
'omit_blanks',
Line 271 ⟶ 361:
'no_column_head',
'short_brackets',
'branch_upwards'
--'one_per_branch'
}) do
if args[v] and (p[v] == nil or type(p[v]) == 'boolean') then
Line 277 ⟶ 367:
end
end
p.namespace = mw.title.getCurrentTitle().namespace
p.flex_tree = {
p.previewnumbers = p.namespace ~= 0 and p.previewnumbers
css = p.flex_tree and {} or {['font-size'] = '50%'},
p.scoreWasher:init(args['score-clean'])
wt = p.flex_tree and '' or '&nbsp;'
p.scoreWasher.demo = args.demoWash and tonumber(args:clean('demoWash', {pattern = '%D'}), 10)
p.scoreSumBox = args['score-boxes'] and args['score-boxes']:match('%d ?%+ ?sum')
p.bold = ({low = 1, high = 2})[args:clean('bold_winner')] or p.scoreSumBox and 2
local sumBox = p.scoreSumBox and 1 or 0
p.scoreBoxes = (tonumber(args:clean('score-boxes', {pattern = '%D'})) or 1) + sumBox
p.scoreSumBox = p.scoreBoxes > 0 and p.scoreSumBox or nil
local boxStyle = p.scoreBoxes > 1 and
(p.scoreSumBox and
setmetatable(
{{}, [p.scoreBoxes] = {'0 1ex'}},
{__call = function(t, i) if t[i] then return nil end return 0 end}
)
or setmetatable(
{},
{__call = function() return 0 end}
)
)
or setmetatable({}, {__call = function() return nil end})
p.colspan = p.scoreBoxes > 0 and (p.scoreBoxes + 1) or nil
local nodeArgs = {
score = p.scoreBoxes - sumBox,
team = {offset = 1 + p.scoreBoxes - sumBox}
}
nodeArgs.all = 1 + nodeArgs.team.offset * 2
nodeArgs.tableSum = {
__add = function(v, t)
if #t == 3 then
return v + nodeArgs.all
end
local s = v
for i, n in ipairs(t) do
s = s + n
end
return s--[[ + (p.scoreSumBox and #t == 3 and -2 or 0) --merging disabled with score boxes, uncomment if enable]]
end
}
nodeArgs.team[1] = 1--constant to be replaced later by new param
nodeArgs.team[2] = nodeArgs.team[1] + nodeArgs.team.offset
nodeArgs.blank = setmetatable({}, nodeArgs.tableSum)
p.unflex_div = mw.html.create'div'
:css{overflow = 'hidden', height = '1ex'}
:wikitext'&nbsp;'
p.flex_tree = setmetatable({},{__index = {
merge = p.flex_tree and p.scoreBoxes == 0,
wt = p.flex_tree and '' or '&nbsp;',
cell = mw.html.create'td'
:node(not p.flex_tree and p.unflex_div or nil)
}})
if args:clean'scroll_height' then
local fontSize, fontUnit = args.style and args.style:match('font%-size *: *(%d+)([^ ]+)')
if fontSize then
local units = {
em = 1,
ex = 2,
['%'] = 0.01
}
fontSize, fontUnit = {fontSize * fontUnit}
end
end
tab
:cssText(table.concat{args.scroll_height and 'padding' or 'margin', ':', fontSize and (math.ceil(fontSize * 10) / 10) or '.9', 'em 2em 1em 1em;border:0;', fontSize and '' or 'font-size:90%;border-collapse:separate;', args.style})
:cssText(
(args.scroll_height and 'padding' or 'margin') .. ':1em 2em 1em 1em;border:0;font-size:90%;'
.. (args.style or '')
)
:attr{cellpadding = 0, cellspacing = 0}
if not p.no_column_head then--headings row
newRow()
head.row = tab.r
:css{['white-space'] = args.scroll_height and 'nowrap'}
newRow()
else
tab.r = tab:tag'tr'
tab.r:tag'td'
end
local scoreWidth = args['score-width'] and mw.text.split(args:clean('score-width', {pattern = '[^%d;]'}), ';') or {}
local sp = {--set column widths
args['team-width'] or 170,
Line 302 ⟶ 447:
p.short_brackets and 4 or 20
}
local scoreWidth = args:clean('score-width', {pattern = '[^%d;]'}) and mw.text.split(args['score-width'], ';') or {}
if p.template then
scoreWidth[1] = tonumber(scoreWidth[1], 10)
p.template = mw.title.new(args.name)
if p.scoreSumBox and #scoreWidth ~= 1 then
p.templateFixedName = (p.template.namespace == 0 and 'Template:' or '') .. p.template.fullText
local _scoreWidth = {}
for k = 1, p.scoreBoxes - 1 do
_scoreWidth[k] = tonumber(scoreWidth[k], 10) or math.ceil(sp[2] * 0.75)
end
setmetatable(scoreWidth, _scoreWidth)
end
 
p.template = p.template and mw.title.new(args:clean('name', {pattern = ''}))
local head_br = {
p.scoreSumBox = args['score-boxes'] and args['score-boxes']:match('%d ?%+ ?sum')
count = 0,
local sumBox = p.scoreSumBox and 1 or 0
compare = function (self, text)
p.scoreBoxes = (tonumber(args:clean('score-boxes', {pattern = '%D'})) or 1) + sumBox
if text and args.scroll_height then
p.scoreSumBox = p.scoreBoxes > 0 and p.scoreSumBox or nil
local _, count = text:gsub('<br[ >/]', '%1')
p.colspan = p.scoreBoxes > 0 and (p.scoreBoxes + 1) or nil
self.count = math.max(self.count, count)
local nodeArgs = {
score = p.scoreBoxes - sumBox,
team = {offset = 1 + p.scoreBoxes - sumBox},
all = 1 + (1 + p.scoreBoxes - sumBox),
tableSum = {
__add = function(v, t)
local s = v
for i, n in ipairs(t) do
s = s + n - (p.scoreSumBox and i > 1 and 1 or 0)
end
return s
end
return text
}
end
}
p.branch_upwards = p.branch_upwards and 0
nodeArgs.team[1] = 1--constant to be replaced later by new param
nodeArgs.team[2] = nodeArgs.team[1] + nodeArgs.team.offset
nodeArgs.blank = setmetatable({}, nodeArgs.tableSum)
for k = 1, p.cols do
if k > 1 then
Line 342 ⟶ 481:
end
if not p.no_column_head then
head.wt = head_br:compare(args[:clean('RD' .. k], {pattern = ''}))
or p.RD[#p.RD + k - p.tCols - 1]
or ('Round of ' .. math.pow(2, p.tCols - k + 1))
drawHead(head.wt)
k == 1 and p.template and mw.getCurrentFrame():expandTemplate{
title = 'tnavbar-header',
args = {head.wt, p.templateFixedName}
} or head.wt
)
end
end
sp.row = tab.r
local step, bump, RD, m, rows = 1, 0, {tot = 0}, {num = 1, phase = 0}, math.pow(2, p.tCols) * 3--Begin body row output
col.tot = math.pow(2, p.tCols - 1)
tab.line = {--reduces concats
local step, bump, bumpBase, rows = 1, 0, mw.html.create'td':attr{colspan = p.colspan}, col.tot * 6--Begin body row output
args.line_px = table.concat{args:clean('line_px') or 3, args.line_px ~= '0' and 'px' or nil}
tab.line = {--reduces concats and 'or' statements
{
[true] = args:clean('.line_px', {ifNil = 3, append = 'px'}),
[false] = 0
},
args.line_px .. :rep(2):gsub('(%a)(%d)', '%1 ..%2', args.line_px1)
}
p['3rdplace'] = p.tCols == p.cols and (p['3rdplace'] or p.cols > 3 and nil == p['3rdplace'] and not p.no_column_head)
for r = 1, rows do
if p['3rdplace'] then
p.textThird = args.Consol or args['RD' .. (p.cols + 1)] or p.RD[4]
local no3rdText = p.no_column_head or p.textThird and p.textThird:match('omit_label')
rowNum.third = math.max(math.pow(2, p.branch_upwards and -3 or p.cols - 2) * 9 + (no3rdText and 4 or 9), no3rdText and 12 or 17, rows)
end
for r = 1, rowNum.third or rows do
newRow(r)
end
p.reuseStr.solid = p.reuseStr.solid or p:saveStr('solid', tab.line[1][true] .., ' solid')
p.cornerDiv = mw.html.create'div':css{height = tab.line[1][true], ['border-right'] = p.reuseStr.solid}
for c = 1, p.cols do
col.c = c
RD.tot = RD.tot + (rows / math.pow(2, c)) / 3
local bumps = bump
if c > 1 and (c < p.cols or p.template) then
if c > 1 then
rowNum[1]:tag'td'
:attr(ccol.tot <= pcol.colstot and+ {rowspan = bumpmath.pow(2, colspan = p.colspan}tCols or- {}c)
if p.branch_upwards then
:wikitext(
bumps = 0
p.flex_tree.wt,
rowNum[1]:tag'td':attr{rowspan = 4}
(p.no_column_head and p.template and c == p.cols and
else
mw.getCurrentFrame():expandTemplate{
rowNum[1]:node(c < p.cols and
title = 'tnavbar-header',
mw.clone(bumpBase):attr{rowspan = bump}
args = {'', p.templateFixedName}
})
or nilend
)
)
end
local bumpscol.top = bumpm.num
p.span = p.tCols > c and bump * 2 or p.branch_upwards or math.max((bump - 1) / 2, 2)
RD.top = m.num
col.color_repechage = p['color_repechage'] and ((c == p.tCols) or ((c == p.tCols-1) and skipMatch[math.pow(2, p.tCols) - 1]))
nodeFunc.bridge.lay[c] = {}
col.show3rd = p['3rdplace'] and c == p.tCols and rowNum.third
p.span = p.tCols > c and (bump * 2) or math.max((bump - 1) / 2, 2)
local colorFinal, bumpMid = p.color and c == p.tCols, p.span > 0 and mw.clone(bumpBase):attr{rowspan = p.span} or nil
for r = 1, rows, 2 do
for r = 1, col.show3rd or rows, 2 do
RD.r = r + bumps
if rowNum[RDm.r] and= m.numr <= RD.tot+ thenbumps
if col.show3rd or rowNum[m.r] and m.num <= col.tot then
if m.phase == 0 then
m.showBox = setmetatable({1, pnodeArgs.colspan or 1team.offset, pnodeArgs.colspan or 1team.offset}, nodeArgs.tableSum)
if nodeFunc:scanPattern(args, step, m.num) then
nodeFunc.called = {}
nodeFuncm.helper.infoavailable = {true
else
occupied = {{},{}},
totalCellsm.available = 6 * (p.colspan or 1),nil
row = RD.r,
col = c,
first = RD.top,
num = m.num,
gap = p.span
}
end
elseif nodeFunc.pattern and m.phase == 1 then
if nodeFunc.called.canvas or nodeFunc.called.bridge then
tab.r:wikitext(nodeFunc.nonFunc)
step = step + 1
end
end
Line 417 ⟶ 549:
end
end
local startcanvas = RD.r + (nodeFunc.pattern and nodeFunc.called.canvas and 6 or 0)
rowNum[startm.r + (canvas or 0)]:tag'td':attr{rowspan = maxSpan((start > RD.rcanvas and 0 or 6) + bump * 2, startm.r + (canvas or 0), rows), colspan = p.colspan}
elseif m.phase == 2 then
if nodeFunc.pattern and (nodeFunc.called.bridge or nodeFunc.called.canvas) then
step = step + 1
end
m.num = m.num + 1
step = step + (p.omit_blanks and 0 or nodeArgsm.allshowBox)
bumps = bumps + (col.show3rd and 0 or maxSpan(p.span, RDm.r, rows))
end
elseif m.phase == 0 then
Line 431 ⟶ 566:
end
end
if not nodeFuncm.helper.freeCells()available == false then
step = step + 1
m.showBox = nodeArgs.blank
step = step + 1
end
end
if m.showBox[1] then
if col.show3rd then
dpBox(nodeFunc.pattern and nodeFunc.nonFunc or args[step], RD.r, skipMatch[m.num])
col.show3rd = (m.num - col.top) * 2
if p.previewnumbers then
if col.show3rd == 2 then
p.namespace = p.namespace or mw.title.getCurrentTitle().namespace
if p.namespace ~= 0textThird:match('omit_label') then
tab p.r:tag'div'textThird = nil
end
if rowNum[rows + 1] and p.cols > 1 then --if 3rd place extends below bottom cell
rowNum[rows + 1]:tag'td':attr{
rowspan = m.r + 9 - rows - (text and 0 or 2),
colspan = (p.cols - 1) * (3 + p.scoreBoxes)
}
end
if p.tCols == 1 then
bumps = p.textThird and 3 or 0
elseif p.branch_upwards then
r = 7
bumps = p.textThird and 2 or 0
end
m.r = r + bumps
if p.textThird then
drawHead(p.textThird, m.r)
bumps = bumps + 2
m.r = r + bumps
end
end
end
dpBox(nodeFunc.pattern and nodeFunc.nonFunc or args[step], m.r)
if p.previewnumbers then
rowNum[m.r].nodes[#rowNum[m.r].nodes]
:tag'div'
:css{
float = 'left',
Line 450 ⟶ 610:
:wikitext(m.num)
:attr{title = 'Number only visible outside article space (e.g. template) when |numberpreview=yes'}
end
end
end
Line 460 ⟶ 619:
table.insert(m.nonEmpty, i)
end
end
if p.scoreSumBox then
table.insert(m.nonEmpty, {-step, -step - 1})
end
if p.bold and m.showBox[2] and m.showBox[3] and not unBold[m.num] then
Line 469 ⟶ 625:
clean = {}
}
local notSummed = not p.scoreSumBox or #m.nonEmpty < 2
for s, i in ipairs(m.nonEmpty) do
m.bold.clean[s] = {p.scoreWasher:main(args[i[1]]), p.scoreWasher:main(args[i[2]])}
if p.scoreSumBox and s == #m.nonEmpty then
argsm.bold.box[i[1s]], args= notSummed and boldWin(m.bold.clean[is][2]1], = cleanSum(m.bold.clean[s][2]) or callableEmpty
m.bold.box[s] = boldWin(args[i[1]], args[i[2]])
else
m.bold.clean[s] = {cleanPts(args[i[1]]), cleanPts(args[i[2]])}
m.bold.box[s] = boldWin(m.bold.clean[s][1], m.bold.clean[s][2])
end
end
mif p.bold.winscoreSumBox =and m.bold.boxnonEmpty[#m.nonEmpty2] or {}then
local i = {-step, -step - 1}
table.insert(m.nonEmpty, i)
args[i[1]], args[i[2]] = p.scoreWasher.sum(m.bold.clean)
m.bold.box[p.scoreBoxes] = boldWin(args[i[1]], args[i[2]])
end
getmetatable(boxStyle).__index = p.scoreSumBoxes and {[#m.nonEmpty] = boxStyle[p.scoreBoxes]}
m.bold.win = m.bold.box[#m.nonEmpty] or callableEmpty
else
m.bold = nilinfiniteEmpty
end
end
else
if m.showBox[m.phase] then
if col.color_repechage then
local base = {color = p.color and c == p.cols and m.phase}
col.color_repechage = 2
teamBox(args[step + nodeArgs.team[m.phase]], RD.r, setmetatable(base, {__index = {bold = m.bold and m.bold.win[m.phase]}}))
end
if p.bold then
if m.bold.win(m.phase) and (colorFinal or col.color_repechage) then
color_index = 1 + (col.show3rd or 0) + (col.color_repechage or 0)
elseif m.bold.box[#m.nonEmpty] then
color_index = 2 + (col.show3rd or 0) + (col.color_repechage or 0)
else
color_index = 4
end
p.teamBoxCSS = (colorFinal or col.color_repechage) and
{border = p.teamBoxNormal.border, ['background-color'] = p.bgColor[color_index], color = p.textColor[color_index]}
or p.teamBoxNormal
else
p.teamBoxCSS = (colorFinal or col.color_repechage) and
{border = p.teamBoxNormal.border, ['background-color'] = p.bgColor[m.phase + (col.show3rd or 0) + (col.color_repechage or 0)], color = p.textColor[m.phase + (col.show3rd or 0) + (col.color_repechage or 0)]}
or p.teamBoxNormal
end
local f = {phase = m.phase, bold = m.bold.win(m.phase)}
teamBox(args[step + nodeArgs.team[m.phase]], m.r, f)
f[1] = 'center'
if p.colspan then
if #m.nonEmpty == 0[1] then
local loneSum
if #m.nonEmpty < p.scoreBoxes then
loneSum = #m.nonEmpty == 1 and boxStyle[p.scoreBoxes]
tab.r:attr{colspan = 1 + p.scoreBoxes - #m.nonEmpty}
end
for s, i in ipairs(m.nonEmpty) do
f.borderLeft = boxStyle(s)
f.sumBox = loneSum or boxStyle[s]
f.bold = m.bold.box[s](m.phase)
teamBox(args[i[m.phase]], m.r, f)
end
else
for s = 1, p.scoreBoxes do
teamBox('', RDf.r,borderLeft base= boxStyle(s)
teamBox(nil, m.r, f)
end
elseif #m.nonEmpty < p.scoreBoxes then
tab.r:attr{colspan = 1 + p.scoreBoxes - #m.nonEmpty}
end
for s, i in ipairs(m.nonEmpty) do
teamBox(args[i[m.phase]], RD.r, setmetatable(base, {__index = {'center', bold = m.bold and m.bold.box[s][m.phase]}}))
end
end
end
if m.phase == 2 then
col.show3rd = col.show3rd ~= 2 and col.show3rd or nil
if p.scoreWasher.demo and p.scoreWasher.demo == m.num and p.namespace ~= 0 then
table.insert(m.bold.clean, 1, {args[step + nodeArgs.team[1]], args[step + nodeArgs.team[2]]})
return table.concat{
'Score data for match specified by <code>|demoWash=</code>:<br>',
mw.dumpObject{scores = m.bold.clean, cycles = p.scoreWasher.cycles, sum = p.scoreSumBox and {m.nonEmpty[#m.nonEmpty][1], m.nonEmpty[#m.nonEmpty][1]}},
'<table>',
tostring(sp.row), '<tr>',
tostring(rowNum[m.r - 4]), '<tr>',
tostring(rowNum[m.r - 2]), '<tr>',
tostring(rowNum[m.r]), '</table>',
}
end
if nodeFunc.orphan.num == m.num then
skipMatch[m.num] = 'orphan'
end
step = step + (m.showBox.unchanged and nodeArgs.full or m.showBox)
m.num = m.num + 1
if bump > 0 and rowNum[RDm.r + 2] and not (nodeFunc.pattern and nodeFunc.called.canvas) then
bumps = bumps + p.span
rowNum[RDm.r + 2]:tag'td':attr{rowspan = p.span, colspan = p.colspan}node(bumpMid)
end
r = r + (col.show3rd or bump)
end
end
Line 518 ⟶ 718:
if p.cols > c then--draw lines to next round
p.unit = bump + 3
bump = (rows3 - rows /* math.pow(2, c)) / math.pow(2, p.tCols - c)3
bumps = p.branch_upwards and 4 or (p.unit + 1)
rowNum[1]
:tag'td':attr{rowspan = bumps}
if not p.branch_upwards then
:tag'td'
rowNum[1]:tag'td'
:attr{rowspan = bump + 4}
:attr{rowspan = (p.branch_upwards or bump) + 4}
:css(nodeFunc.bridge.lay[c][0] and
:css(nodeFunc.bridge.lay[c](0) and
{['border-right'] = p.reuseStr.solid}
or {}
)
RD.n = 0end
col.n = 0
col.t2 = nil
for r = bumps + 1, rows, p.unit * 2 do
tab.r = rowNum[r]:tag'td'
local interval = ((r - bumps - 1) / (p.unit * 2)) % 4
if interval % 2 == 0 then
--RDcol.t and RDcol.t2 control whether lines are drawn
RDcol.t = RDcol.t2 or skipMatch[RDcol.tot + RDcol.n / 2 + 1] and 3 or ((skipMatch[RDcol.top] and 1 or 0) + (skipMatch[RDcol.top + 1] and 2 or 0))
RDcol.n = RDcol.n + 2
RDcol.t2 = skipMatch[RDcol.tot + RDcol.n / 2 + 1] and 3 or ((skipMatch[RDcol.top + RDcol.n] and 1 or 0) + (skipMatch[RDcol.top + RDcol.n + 1] and 2 or 0))
if RDcol.t == 0 then --draws the ']' when a PAIR of matches needs lines
tab.r
:attr{rowspan = maxSpan(p.unit * 2, r, rows)}
:css(skipMatch[RDcol.tot + RDcol.n / 2] and {} or {
border = p.reuseStr.solid,
['border-left'] = 0
})
else --draws the lines when only top OR bottom match need lines
else
tab.r
:attr{rowspan = maxSpan(p.unit, r, rows)}
:cssText(RDcol.t == 2 and
(p.reuseStr.topRight or p:saveStr('topRight', 'border-width:' .., tab.line[2] .., ' 0 0;border-style:solid'))
or RDcol.t == 1 and (nodeFunc.bridge.lay[c][RD(col.n - 2]) and
(p.reuseStr.right or p:saveStr('right', ';border-right:' .., p.reuseStr.solid))
or 'vertical-align:bottom'
)
or nil
)
:node(RDcol.t == 1 and interval > 0 and not nodeFunc.bridge.lay[c][RD(col.n - 2]) and p.cornerDiv)
rowNum[r + (p.branch_upwards and (4 - bump) or p.unit)]:tag'td'
mw.html.create'div'
:css{height = tab.line[1][true], ['border-right'] = p.reuseStr.solid}
)
rowNum[r + p.unit]:tag'td'
:attr{rowspan = maxSpan(p.unit, r + p.unit, rows)}
:cssText(RDcol.t == 1 and
(p.reuseStr.bttmRght or p:saveStr('bttmRght', 'border-width:0 ' .., tab.line[2] .., ' 0;border-style:solid'))
or RDcol.t == 2 and (nodeFunc.bridge.lay[c][RD(col.n + 2]) and
(p.reuseStr.right or p:saveStr('right', ';border-right:' .., p.reuseStr.solid))
or 'vertical-align:top'
)
or nil
)
:node(RDcol.t == 2 and interval ~= 2 and not nodeFunc.bridge.lay[c][RD(col.n + 2]) and p.cornerDiv)
mw.html.create'div'
:css{height = tab.line[1][true], ['border-right'] = p.reuseStr.solid}
)
end
RDcol.t = {
RDcol.t < 3,
rowNum[r + p.unit * 5] and RDcol.t2 < 3 or false
}
rowNum[r + (p.branch_upwards or p.unit)]:tag'td'
:attr{rowspan = maxSpan(p.unit * 4, r + (p.branch_upwards and (4 - bump) or p.unit), rows)}
:css(interval == 0 and (RDcol.t[1] or RDcol.t[2]) and {
['border-width'] = table.concat{tab.line[1][RDcol.t[1]] .., ' 0 ' .., tab.line[1][RDcol.t[2]]},
['border-style'] = 'solid'
} or {})
Line 587 ⟶ 784:
tab.r
:attr{rowspan = maxSpan(p.unit * 2, r, rows)}
:css(nodeFunc.bridge.lay[c][RD(col.n]) and
{['border-right'] = p.reuseStr.solid}
or {}
Line 595 ⟶ 792:
end
end
local lock_height = (head_br.count or 0) + 1
if p.tCols == p.cols and (p['3rdplace'] or p.cols > 3 and p['3rdplace'] == nil and not p.no_column_head) then--begin 3rd place
return args.scroll_height and
RD.r = rows / 2 + 4 + math.max(p.span, 3)
for r = rows + 1, RD.r + 7 do
newRow(r)
end
if rowNum[rows + 1] and p.cols > 1 then --if 3rd place extends below bottom cell
rowNum[rows + 1]:tag'td':attr{
rowspan = RD.r + 7 - rows,
colspan = (p.cols - 1) * 4
}
end
drawHead(args.Consol or args['RD' .. (p.cols + 1)] or p.RD[4], RD.r)
dpBox(args[step], RD.r + 2)
m.bold = boldWin(cleanPts(args[step + 2]), cleanPts(args[step + 4]), unBold[m.num])
for k, v in ipairs({
{4, {color = p.color and 3, bold = m.bold[1]}},
{4, {'center', color = p.color and 3, bold = m.bold[1]}},
{6, {bold = m.bold[2]}},
{6, {'center', bold = m.bold[2]}}
}) do
teamBox(args[step + k], RD.r + v[1], v[2])
end
end
return args:clean('scroll_height') and
mw.html.create'div'
:cssText'border-bottom:1px solid #eee;display:inline-block'
Line 624 ⟶ 799:
:css{
overflow = 'hidden',
height = lock_height * 1.4 + 1.6 .. '3emem',
['border-bottom'] = 'inherit',
['margin-right'] = '17px'
Line 636 ⟶ 811:
}
:node(not (p.scroll_head_unlock or p.no_column_head) and
tab:css({['margin-top'] = math.floor(-10 * (lock_height * 1.4 + 1.6)/(fontSize or .9)) / 10 .. 'em', ['padding-3emtop')] = '-3px'}
or tab
)
Line 643 ⟶ 818:
end
 
--[[local standard = {
'beta' = {
bold_winner = 'high',
omit_blanks = 'yes',
auto_3rd = 'yes'
}
}--]]
function p.main(frame, columns)
local args = require'Module:Arguments'.getArgs(frame, {trim = false})
args.columns = args.columns or columns
return p._main(args, frame)
end
 
function p.seed(frame)
local parent = frame:getParent() or frame
local function arg(k, alt)
return parent.args[k] or frame.args[k] or alt
end
local padding, width = arg(2, p.teamBoxPadding()), arg(3, arg('widescore') and 40 or 30)
padding = tonumber(padding) and tonumber(padding) .. 'px' or padding
width = tonumber(width) and tonumber(width) .. 'px' or width
return mw.html.create'div'
:css{
margin = ('-1px %s -1px -0.7ex'):format(padding, padding),
float = 'left',
['background-color'] = p.bgColor.head,
border = '1px solid #aaa',
color = p.textColor.head,
['text-align'] = 'center',
width = width
}
:wikitext(arg(1, '&nbsp;'))
end