Module:RoundN
Jump to navigation
Jump to search
Documentation for this module may be created at Module:RoundN/doc
local p = { RD = { 'Quarter-finals', 'Semi-finals', 'Final', 'Third place' }, -- 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, ...) if not self.reuseStr[name] then self.reuseStr[name] = table.concat{...} end return self.reuseStr[name] end } --Provides a convenient naming shortcut up to {{#invoke:RoundN|N512}} = {{invoke:RoundN|main|columns = 9}} 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 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) self.pattern = nil if args[step] then self.pattern, self.nonFunc = string.match(esc.bs:text(args[step]), '^node_function{(.-)}(.*)') end if self.pattern then for k, v in pairs(esc.comma) do self.pattern = self.pattern:gsub(k, v) end self.nonFunc = self.nonFunc and esc.bs:undo(self.nonFunc) self.pattern = mw.text.split(self.pattern, '%s*,%s*') for k, v in ipairs(self.pattern) do local func, arg = string.match(v, '^(%w+)%(?([^%)]*)') if func and self[func] and self[func].main then self.pattern[k] = func if arg then for x, y in pairs(esc.comma) do arg = esc.bs:undo(arg):gsub(y:gsub('%%%d', ''), x:match('%)([^%(])%(') or x:gsub('\\', '')) end self[func].arg = self[func].arg or {} self[func].arg[m.num] = arg end end end end return self.pattern end, helper = { topBranch = function()--node is top of fork if top is 0 return (m.num - col.top) % 2 end, addText = function(text) if text and text ~= '' then tab.r:wikitext(text) end end }, line = {--this node is omitted and replaced with a line main = function(x) local h = p.getNodeFunc() if m.available then local text, topId, isTop, notTop = h.line.arg[m.num] or '', h.topBranch() isTop = topId == 0 notTop = {[isTop and 1 or 0] = p.reuseStr.solid} for k = 0, 1 do tab.r = rowNum[m.r + k * 4]:tag'td' :css(notTop[k] and {[isTop and 'border-top' or 'border-bottom'] = notTop[k]} or {} ) :attr{ rowspan = ({[0] = 4, 2})[k], colspan = p.colspan } h.addText(text or h.nonFunc) text = nil end m.available = false else return nil end return x end }, bridge = {--Draw a line to the neighboring node in the same column that is not connected to the current node main = function(x) local h = p.getNodeFunc() h.bridge.lay[col.c][m.num - col.top + 1 + (h.topBranch() == 1 and 1 or -1)] = true h.addText(nonFunc) return x end, lay = setmetatable({}, p.nilAsTab) }, canvas = {--Merges all cells in node. Content will be the next parameter. main = function(x) local h = p.getNodeFunc() if m.available then tab.r = rowNum[m.r]:tag'td' :attr{ rowspan = 6, colspan = p.colspan } h.addText(h.nonFunc) m.available = false return x else return nil end end }, orphan = {--sets a flag for skipMatch to be set by p._main main = function(x) p.getNodeFunc().orphan.num = m.num return x end }, skipAllowed = {--table of supported node functions when node is skipped (i.e. by skipmatch) bridge = true, canvas = true } } setmetatable(nodeFunc.helper, {__index = nodeFunc}) function p.getNodeFunc() return nodeFunc.helper end 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 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 td:wikitext(text):css{ ['text-align'] = 'center', border = '1px solid #aaa', ['background-color'] = p.bgColor.head, color = p.textColor.head } end end local function spacer(width) tab.r:tag'td' :attr{width = width} :wikitext(p.no_column_head and '' or ' ') end local function dpBox(v, r) p.dpBoxBase = p.dpBoxBase or mw.html.create'td':attr{rowspan = 2, colspan = p.colspan} 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)) end end p.scoreWasher = { numberFormat = '%-?%d+%.?%d*', main = function (self, s) if s then for _, cycle in ipairs(self.cycles) do s = s:gsub(unpack(cycle)) end if p.scoreSumBox and self.plus then local t = 0 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 {'—', '—'} or sum) end } local function boldWin(s1, s2) return setmetatable( 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 p.teamBoxPadding = function() return '.6ex' end p.teamBoxPadTab = {padding = '0 ' .. p.teamBoxPadding()} p.teamBoxNormal = {border = '1px solid #aaa', ['background-color'] = p.bgColor[4], color = p.textColor[4]} 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{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 ' ' 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) 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', {pattern = '%D'})) p.tCols = (tonumber(args:clean('final_RDs_excluded', {pattern = '%D'})) or 0) + p.cols local matchPer = { pattern = '%d*per%d+[%-x]%d+', vals = '(%d*)per(%d+)([%-x])(%d+)' } 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 = ({skip = skipMatch, manualbold = unBold})[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, {ifNil = ''}):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({--more args to boolean 'widescore', 'color', 'color_repechage', '3rdplace', 'omit_blanks', 'scroll_head_unlock', 'previewnumbers', 'flex_tree', 'no_column_head', 'short_brackets', 'branch_upwards' }) 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', {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' ' p.flex_tree = setmetatable({},{__index = { merge = p.flex_tree and p.scoreBoxes == 0, wt = p.flex_tree and '' or ' ', 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}) :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 sp = {--set column widths args['team-width'] or 170, p.widescore and 40 or 30, p.short_brackets and 6 or 15, p.short_brackets and 4 or 20 } local scoreWidth = args:clean('score-width', {pattern = '[^%d;]'}) 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 = { count = 0, compare = function (self, text) if text and args.scroll_height then local _, count = text:gsub('<br[ >/]', '%1') self.count = math.max(self.count, count) end return text end } 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{colspan = 2} 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, {pattern = ''})) 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{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.line_px, [false] = 0 }, args.line_px:rep(2):gsub('(%a)(%d)', '%1 %2', 1) } 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{height = tab.line[1][true], ['border-right'] = p.reuseStr.solid} 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{rowspan = 4} else rowNum[1]:node(c < p.cols and mw.clone(bumpBase):attr{rowspan = bump} ) 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{rowspan = p.span} 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({1, nodeArgs.team.offset, nodeArgs.team.offset}, 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{rowspan = maxSpan((canvas and 0 or 6) + bump * 2, m.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 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{ 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', border = '1px solid red', padding = '0 .5ex', ['color'] = 'red' } :wikitext(m.num) :attr{title = 'Number only visible outside article space (e.g. template) when |numberpreview=yes'} end end if p.colspan then m.nonEmpty = {} for s = step + 2, step + nodeArgs.team.offset do local i = {s, s + nodeArgs.team.offset} 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 = { box = {}, 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]])} 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 = {-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 = 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 {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[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 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, {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 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{rowspan = bumps} if not p.branch_upwards then rowNum[1]:tag'td' :attr{rowspan = (p.branch_upwards or bump) + 4} :css(nodeFunc.bridge.lay[c](0) and {['border-right'] = p.reuseStr.solid} or {} ) end col.n = 0 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 tab.r :attr{rowspan = maxSpan(p.unit * 2, r, rows)} :css(skipMatch[col.tot + col.n / 2] and {} or { border = p.reuseStr.solid, ['border-left'] = 0 }) else tab.r :attr{rowspan = maxSpan(p.unit, r, rows)} :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{rowspan = maxSpan(p.unit, r + p.unit, rows)} :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 = { col.t < 3, rowNum[r + p.unit * 5] and col.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 (col.t[1] or col.t[2]) and { ['border-width'] = table.concat{tab.line[1][col.t[1]], ' 0 ', tab.line[1][col.t[2]]}, ['border-style'] = 'solid' } or {}) else tab.r :attr{rowspan = maxSpan(p.unit * 2, r, rows)} :css(nodeFunc.bridge.lay[c](col.n) and {['border-right'] = p.reuseStr.solid} 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{ overflow = 'hidden', height = lock_height * 1.4 + 1.6 .. 'em', ['border-bottom'] = 'inherit', ['margin-right'] = '17px' } :node(mw.clone(tab)) ) :tag'div' :css{ ['overflow-y'] = 'scroll', ['max-height'] = tonumber(args.scroll_height, 10) and args.scroll_height .. 'px' or args.scroll_height } :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-top'] = '-3px'} or tab ) :done() or tab 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) 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, ' ')) end return p