Module:RoundN explained

local p =

--Provides a convenient naming shortcut up to = for columns = 1, 9 do 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 insensitiveend

--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 = --never assign a value to these or they will stop being emptylocal infiniteEmpty = setmetatable -- infiniteEmpty[1][2][3]...[infinity] = local callableEmpty = setmetatable(p.nilAsTab)

local rowNum, head, m, col, tab, esc =,,,, mw.html.create'table', local nodeFunc =

setmetatable(nodeFunc.helper,)function p.getNodeFunc return nodeFunc.helperend

local function newRow(bodyRow) local first = p.flex_tree.merge and mw.clone(p.flex_tree.cell) or p.flex_tree.cell tab.r = tab:tag'tr' :node(first) 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 endend

local function drawHead(text, row3rd) local td = (row3rd and rowNum[row3rd]:tag'td':attr or head.row:tag'td') :attr if text ~= 'omit_label' then td:wikitext(text):css endend

local function spacer(width) tab.r:tag'td' :attr :wikitext(p.no_column_head and or ' ')end

local function dpBox(v, r) p.dpBoxBase = p.dpBoxBase or mw.html.create'td':attr if not v then p.dpBoxEmpty = p.previewnumbers and mw.clone(p.dpBoxBase) or p.dpBoxEmpty or mw.clone(p.dpBoxBase):wikitext(p.flex_tree.wt) rowNum[r]:node(p.dpBoxEmpty) else rowNum[r]:node(mw.clone(p.dpBoxBase):wikitext(v)) endend

p.scoreWasher =

local function boldWin(s1, s2) return setmetatable(p.bold and s1 ~= s2 and (math[({'min', 'max'})[p.bold]](s1, s2)

s1 and or) 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 valuep.teamBoxPadding = function return '.6ex'endp.teamBoxPadTab = p.teamBoxNormal = local function teamBox(v, r, f) if p.flex_tree.merge and not v and f.phase

2 then for i = -2, 0 do if rowNum[r + i].first.unchanged then rowNum[r + i].first.unchanged = nil rowNum[r + i].first:node(p.unflex_div) end end tab.r:attr:css else if not p.bold then --backwards compatability (wikitemplates bold each arg individually) local hasBold, b = tostring(v):gsub("([^']*)([^']*)", '%1%2') if b

1 then v = hasBold end end local cell if f[1] then cell = f.sumBox and f.sumBox[1] and or cell['text-align'] = v and f[1] else cell = p.teamBoxPadTab end local text = v or f[1] and or ' ' if f.bold then text = mw.ustring.gsub(text, '(%(%[%[[^%[%]]*%]%]%))', '

%1') end tab.r = rowNum[r]:tag'td' :css(p.teamBoxCSS) :css(cell) :attr :node(mw.html.create(f.bold and 'b'):wikitext(text)) endend

function p._main(args) function args:clean(key, params)--prevent html comments from breaking named args and reduces repeat concatenation params = params or local clean = args[key] or params.ifNil 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 args[key] = params.keepOld and args[key] or clean return clean end p.cols = tonumber(args:clean('columns',)) p.tCols = (tonumber(args:clean('final_RDs_excluded',)) or 0) + p.cols local matchPer = local skipMatch, unBold =, --(skip|manualbold)match# to boolean for k, _ in pairs(args) do local mType, mNum = string.match(k, '^(%l+)match(%d*)$') mType, mNum = [mType], tonumber(mNum) if mType then if mNum then mType[mNum] = args:clean(k)

'yes' or args[k]

'true' else for pattern in args:clean(k,):gfind(matchPer.pattern) do local d1, period, op, d2 = pattern:match(matchPer.vals) d1 = tonumber(d1) or 1 d2 = op

'-' and d2 or (d1 + period * (d2 - 1)) for y = d1, d2, period do mType[y] = true end end for _, x in ipairs(mw.text.split(args[k]:gsub(matchPer.pattern, ):gsub('[;%-%a][;%-%a]+', ';'):match('^;*(.-)[;%-]*$'), ';')) do x = mw.text.split(x, '-') for y = tonumber(x[1]) or 1, tonumber(x[2] or x[1]) or 0 do mType[y] = true end end end end end for _, v in ipairs do if args[v] and (p[v]

nil or type(p[v])

'boolean') then p[v] = args:clean(v)

'yes' or args[v]

'true' end end p.namespace = mw.title.getCurrentTitle.namespace p.previewnumbers = p.namespace ~= 0 and p.previewnumbers p.scoreWasher:init(args['score-clean']) p.scoreWasher.demo = args.demoWash and tonumber(args:clean('demoWash',), 10) p.scoreSumBox = args['score-boxes'] and args['score-boxes']:match('%d ?%+ ?sum') p.bold = [args:clean('bold_winner')] or p.scoreSumBox and 2 local sumBox = p.scoreSumBox and 1 or 0 p.scoreBoxes = (tonumber(args:clean('score-boxes',)) or 1) + sumBox p.scoreSumBox = p.scoreBoxes > 0 and p.scoreSumBox or nil local boxStyle = p.scoreBoxes > 1 and (p.scoreSumBox and setmetatable or setmetatable ) or setmetatable p.colspan = p.scoreBoxes > 0 and (p.scoreBoxes + 1) or nil local nodeArgs = nodeArgs.all = 1 + nodeArgs.team.offset * 2 nodeArgs.tableSum = 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 :wikitext' ' p.flex_tree = setmetatable if args:clean'scroll_height' then local fontSize, fontUnit = args.style and args.style:match('font%-size *: *(%d+)([^ ]+)') if fontSize then local units = fontSize, fontUnit = end end tab :cssText(table.concat) :attr if not p.no_column_head then--headings row newRow head.row = tab.r :css newRow else tab.r = tab:tag'tr' tab.r:tag'td' end local sp = local scoreWidth = args:clean('score-width',) and mw.text.split(args['score-width'], ';') or scoreWidth[1] = tonumber(scoreWidth[1], 10) if p.scoreSumBox and #scoreWidth ~= 1 then 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

local head_br = p.branch_upwards = p.branch_upwards and 0 for k = 1, p.cols do if k > 1 then spacer(sp[3]) spacer(sp[4]) if not p.no_column_head then head.row:tag'td':attr end end spacer(sp[1]) for s = 1, p.scoreBoxes do spacer(#scoreWidth

1 and scoreWidth[1] or scoreWidth[s] or sp[2]) end if not p.no_column_head then head.wt = head_br:compare(args:clean('RD' .. k,)) or p.RD[#p.RD + k - p.tCols - 1] or ('Round of ' .. math.pow(2, p.tCols - k + 1)) drawHead(head.wt) end end sp.row = tab.r col.tot = math.pow(2, p.tCols - 1) local step, bump, bumpBase, rows = 1, 0, mw.html.create'td':attr, col.tot * 6--Begin body row output args.line_px = table.concat tab.line = p['3rdplace'] = p.tCols

p.cols and (p['3rdplace'] or p.cols > 3 and nil

p['3rdplace'] and not p.no_column_head) 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:saveStr('solid', tab.line[1][true], ' solid') p.cornerDiv = mw.html.create'div':css for c = 1, p.cols do col.c = c local bumps = bump if c > 1 then col.tot = col.tot + math.pow(2, p.tCols - c) if p.branch_upwards then bumps = 0 rowNum[1]:tag'td':attr else rowNum[1]:node(c < p.cols and mw.clone(bumpBase):attr ) end end col.top = m.num p.span = p.tCols > c and bump * 2 or p.branch_upwards or math.max((bump - 1) / 2, 2) col.color_repechage = p['color_repechage'] and ((c

p.tCols) or ((c

p.tCols-1) and skipMatch[math.pow(2, p.tCols) - 1])) col.show3rd = p['3rdplace'] and c

p.tCols and rowNum.third local colorFinal, bumpMid = p.color and c

p.tCols, p.span > 0 and mw.clone(bumpBase):attr or nil for r = 1, col.show3rd or rows, 2 do m.r = r + bumps if col.show3rd or rowNum[m.r] and m.num <= col.tot then if m.phase

0 then m.showBox = setmetatable(nodeArgs.tableSum) if nodeFunc:scanPattern(args, step) then nodeFunc.called = m.available = true else m.available = nil end end if skipMatch[m.num] then if m.phase

0 then if nodeFunc.pattern then for x, y in ipairs(nodeFunc.pattern) do if nodeFunc.skipAllowed[y] then nodeFunc.called[y] = nodeFunc[y].main(x) end end end local canvas = nodeFunc.pattern and nodeFunc.called.canvas and 6 rowNum[m.r + (canvas or 0)]:tag'td':attr 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 m.showBox) bumps = bumps + (col.show3rd and 0 or maxSpan(p.span, m.r, rows)) end elseif m.phase

0 then if nodeFunc.pattern then for x, y in ipairs(nodeFunc.pattern) do if nodeFunc[y] and nodeFunc[y].main then nodeFunc.called[y] = nodeFunc[y].main(x) end end if m.available

false then m.showBox = nodeArgs.blank step = step + 1 end end if m.showBox[1] then if col.show3rd then col.show3rd = (m.num - col.top) * 2 if col.show3rd

2 then if p.textThird:match('omit_label') then p.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 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 :wikitext(m.num) :attr end end if p.colspan then m.nonEmpty = for s = step + 2, step + nodeArgs.team.offset do local i = if args[i[1]] or args[i[2]] then table.insert(m.nonEmpty, i) end end if p.bold and m.showBox[2] and m.showBox[3] and not unBold[m.num] then m.bold = local notSummed = not p.scoreSumBox or #m.nonEmpty < 2 for s, i in ipairs(m.nonEmpty) do m.bold.clean[s] = m.bold.box[s] = notSummed and boldWin(m.bold.clean[s][1], m.bold.clean[s][2]) or callableEmpty end if p.scoreSumBox and m.nonEmpty[2] then local i = 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.bold.win = m.bold.box[#m.nonEmpty] or callableEmpty else m.bold = infiniteEmpty end end else if m.showBox[m.phase] then if col.color_repechage then col.color_repechage = 2 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 or p.teamBoxNormal else p.teamBoxCSS = (colorFinal or col.color_repechage) and or p.teamBoxNormal end local f = teamBox(args[step + nodeArgs.team[m.phase]], m.r, f) f[1] = 'center' if p.colspan then if m.nonEmpty[1] then local loneSum if #m.nonEmpty < p.scoreBoxes then loneSum = #m.nonEmpty

1 and boxStyle[p.scoreBoxes] tab.r:attr 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 f.borderLeft = boxStyle(s) teamBox(nil, m.r, f) end 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,) return table.concat end if nodeFunc.orphan.num

m.num then skipMatch[m.num] = 'orphan' end step = step + m.showBox m.num = m.num + 1 if bump > 0 and rowNum[m.r + 2] and not (nodeFunc.pattern and nodeFunc.called.canvas) then bumps = bumps + p.span rowNum[m.r + 2]:node(bumpMid) end r = r + (col.show3rd or bump) end end m.phase = (m.phase + 1) % 3 end end if p.cols > c then--draw lines to next round p.unit = bump + 3 bump = 3 * math.pow(2, c) - 3 bumps = p.branch_upwards and 4 or (p.unit + 1) rowNum[1] :tag'td':attr if not p.branch_upwards then rowNum[1]:tag'td' :attr :css(nodeFunc.bridge.lay[c](0) and or ) end 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 --col.t and col.t2 control whether lines are drawn col.t = col.t2 or skipMatch[col.tot + col.n / 2 + 1] and 3 or ((skipMatch[col.top] and 1 or 0) + (skipMatch[col.top + 1] and 2 or 0)) col.n = col.n + 2 col.t2 = skipMatch[col.tot + col.n / 2 + 1] and 3 or ((skipMatch[col.top + col.n] and 1 or 0) + (skipMatch[col.top + col.n + 1] and 2 or 0)) if col.t

0 then --draws the ']' when a PAIR of matches needs lines tab.r :attr :css(skipMatch[col.tot + col.n / 2] and or) else --draws the lines when only top OR bottom match need lines tab.r :attr :cssText(col.t

2 and p:saveStr('topRight', 'border-width:', tab.line[2], ' 0 0;border-style:solid') or col.t

1 and (nodeFunc.bridge.lay[c](col.n - 2) and p:saveStr('right', ';border-right:', p.reuseStr.solid) or 'vertical-align:bottom' ) or nil ) :node(col.t

1 and interval > 0 and not nodeFunc.bridge.lay[c](col.n - 2) and p.cornerDiv) rowNum[r + (p.branch_upwards and (4 - bump) or p.unit)]:tag'td' :attr :cssText(col.t

1 and p:saveStr('bttmRght', 'border-width:0 ', tab.line[2], ' 0;border-style:solid') or col.t

2 and (nodeFunc.bridge.lay[c](col.n + 2) and p:saveStr('right', ';border-right:', p.reuseStr.solid) or 'vertical-align:top' ) or nil ) :node(col.t

2 and interval ~= 2 and not nodeFunc.bridge.lay[c](col.n + 2) and p.cornerDiv) end col.t = rowNum[r + (p.branch_upwards or p.unit)]:tag'td' :attr :css(interval

0 and (col.t[1] or col.t[2]) and or) else tab.r :attr :css(nodeFunc.bridge.lay[c](col.n) and or ) end end end end local lock_height = (head_br.count or 0) + 1 return args.scroll_height and mw.html.create'div' :cssText'border-bottom:1px solid #eee;display:inline-block' :node(not (p.scroll_head_unlock or p.no_column_head) and mw.html.create'div' :css :node(mw.clone(tab)) ) :tag'div' :css :node(not (p.scroll_head_unlock or p.no_column_head) and tab:css or tab ) :done or tabend

--local standard = --function p.main(frame, columns) local args = require'Module:Arguments'.getArgs(frame,) args.columns = args.columns or columns return p._main(args)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 :wikitext(arg(1, ' '))end

return p