Jump to content

Module:RoundN

विकिपीडिया से
en>Codehydro (had flex_tree backwards) के द्वारा 23:56, 8 जनवरी 2015 के बदलाव
local p = {
	RD = {
		'Quarter Finals',
		'Semi Finals',
		'Final',
		'Third Place'
	},
	nodeActions = {
		line = function(r, top)
			for k = 0, 1 do
				rowNum[r + k * 4]:tag'td'
					:wikitext(p.flextree.wt)
					:css{
						['border-' .. top == 0 and 'bottom' or 'top'] = k == top and '1px solid #000' or nil,
						['font-size'] = p.flex_tree.css['font-size']
					}
					:attr{
						rowspan = k == 0 and 4 or 2,
						colspan = 2
					}
			end
		end
	}
}
local output = mw.html.create'table'
local rowNum, head = {}, {}

--Provides a convenient naming shortcut up to {{#invoke:RoundN|N512}} = {{invoke:RoundN|main|columns = 9}}
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)
	p.tag = output:tag'tr'
	p.tag:tag'td'
		:css(p.flex_tree.css)
		:wikitext(p.flex_tree.wt)
	--Note: Original sets <td height=5>; This method is more flexiable
	if bodyRow then
		table.insert(rowNum, bodyRow, p.tag)
	end
end

function drawHead(text, row3rd)
	(row3rd and rowNum[row3rd]:tag'td':attr{rowspan = 2}
	or head.row:tag'td')
		:attr{colspan = 2}
		:css{
			['text-align'] = 'center',
			border = '1px solid #aaa',
			background = '#f2f2f2'
		}
		:wikitext(text)
end

function spacer(width)
	p.tag:tag'td'
		:attr{width = width}
		:wikitext'&nbsp;'
end

function dpBox(v, r)
	p.tag = rowNum[r]:tag'td'
		:wikitext(v or '&nbsp;')
		:attr{rowspan = 2, colspan = 2}
end

function boldWin(s1, s2,  manual)
	local result = {}
	if p.bold and not manual then
		s1 = tonumber(string.gsub(s1 or '', '%D', ''), 10) or math.huge
		s2 = tonumber(string.gsub(s2 or '', '%D', ''), 10) or math.huge
		result = ({{s1 < s2, s1 > s2}, {s1 > s2, s1 < s2}})[p.bold]
	end
	return result
end

function maxSpan(span, start, rows)
	return math.min(span, math.max(0, rows - start + 1))
end

function teamBox(v, r, f)
	p.old_tag = p.tag
	p.tag = rowNum[r]:tag'td'
		:css{
			border = '1px solid #aaa',
			background = ({'gold', 'silver', '#C96', '#f9f9f9'})[f.color or 4],
			['text-align'] = f[1]
		}
		:attr{rowspan = 2}
	p.tag:tag(f.bold and 'b' or ''):wikitext('&nbsp;' .. (v or ''))
end

function p._main(args, frame)
	function args:clean(key, params)--prevent html comments from breaking some args
		local clean = args[key] and string.gsub(string.gsub(mw.text.decode(args[key]), '<!.+>', ''), '[^%w-;]', '') or nil
		args[key] = (params or {}).keep_old and args[key] or clean
		return clean
	end
	p.cols = tonumber(args:clean('columns'))
	p.bold = ({low = 1, high = 2})[args:clean('bold_winner')]
	local skipMatch, unBold  = {}, {}--(skip|manualbold)match# to boolean
	for k, _ in pairs(args) do
		local mType, mNum = string.match(k, '^(%a+)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'
			elseif ({['-']=1,[';']=1})[string.match(args:clean(k), '%d([-;])%d')] then--match ranges when no skipmatch
				for _, x in ipairs(mw.text.split(args[k], ';')) do
					local m = mw.text.split(x, '-')
					for y = tonumber(m[1] or x) or 1, tonumber(m[2] or m[1] or x) or 0 do
						mType[y] = true
					end
				end
			end
		end
	end
	for _, v in ipairs({
	--more args to boolean
		'widescore',
		'template',
		'color',
		'3rdplace',
		'omit_blanks',
		'scroll_head_unlock',
		'previewnumbers',
		'flex_tree',
		'one_per_branch'
	}) 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.flex_tree = {
		css = p.flex_tree and {} or {['font-size'] = '50%'},
		wt = p.flex_tree and '' or '&nbsp;'
	}
	output
		: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}
	newRow()--headings row
	head.row = p.tag
	local sp = {--set column widths
		args['team-width'] or 170,
		args['score-width'] or (p.widescore and 40) or 30,
		15,
		20
	}
	newRow()
	for k = 1, p.cols do
		if k > 1 then
			spacer(sp[3])
			spacer(sp[4])
			head.row:tag'td':attr{colspan = 2}
		end
		spacer(sp[1])
		spacer(sp[2])
		p.head = args['RD' .. k]
			or p.RD[#p.RD + k - p.cols - 1]
			or ('Round of ' .. math.pow(2, p.cols - k + 1))
		drawHead(
			k == 1 and p.template and frame:expandTemplate{
				title = 'tnavbar-header',
				args = {p.head, 'Template:' .. args.name}
			} or p.head
		)
	end
	local step, bump, bumps, RD, m, rows, ns = 1, 0, 0, {tot = 0}, {num = 1, phase = 0}, math.pow(2, p.cols) * 3, mw.title.getCurrentTitle().namespace--Begin output
	for c = 1, p.cols do
		RD.tot = RD.tot + (rows / math.pow(2, c)) / 3
		if c > 1 then
			rowNum[1]:tag'td'
				:attr{rowspan = bump, colspan = 2}
				:wikitext'&nbsp;'
		end
		bumps = bump
		RD.top = m.num
		for r = 1, rows, 2 do
			if c == 1 then
				newRow(r)
				newRow(r + 1)
			end
			RD.r = r + bumps
			if rowNum[RD.r] and m.num <= RD.tot then
				if skipMatch[m.num] then
					if m.phase == 0 then
						rowNum[RD.r]:tag'td':attr{rowspan = maxSpan(6 + bump * 2, RD.r, rows), colspan = 2}
					elseif m.phase == 2 then
						m.num = m.num + 1
						step = step + (p.omit_blanks and 0 or 5)
						bumps = bumps + maxSpan(bump * 2, RD.r, rows)
					end
				elseif m.phase == 0 then
					local set_node = string.match(args[step] or '', '^set_node ')
					m.nodeActs = {}
					m.showBox = {1, 2, not p.one_per_branch and 2 or nil}
					if set_node then
						local node_args = mw.text.split(args[step + (m.phase - 1) * 2 + 1], ' ')
						for _, y in ipairs(node_args) do
							if p.nodeActions[y] then
								p.nodeActions[y](RD.r, (m.num - RD.top))
								m.nodeActs[y] = true
							end
						end
						step = step + 1
						if m.nodeActs['line'] then
							m.phase = 2
							m.showBox = {}
						end
					end
					if m.showBox[1] then
						dpBox(args[step], RD.r, skipMatch[m.num])
					end
					p.tag:node(args.previewnumbers and ns ~= 0 and
						mw.html.create'div'
							:css{float = 'left', display = 'inline-block', ['margin-left'] = -(math.floor(math.log10(m.num)) + 2) .. 'ex', border = '1px solid', padding = '.5ex', background = '#fff'}
							:wikitext(m.num)
					)
					m.bold = boldWin(args[step + 2], args[step + 4], not (m.showBox[2] or m.showBox[3]) or unBold[m.num])
				else
					if m.showBox[m.phase] then
						teamBox(args[step + (m.phase - 1) * 2 + 1], RD.r, {color = p.color and c == p.cols and m.phase, bold = m.bold[m.phase]})
						teamBox(args[step + (m.phase - 1) * 2 + 2], RD.r, {'center', color = p.color and c == p.cols and m.phase, bold = m.bold[m.phase]})
					end
					if p.one_per_branch then
						m.phase = 2
						p.tag:attr(m.showBox[2] and {rowspan = 4} or nil)
					end
					if m.phase == 2 then
						step = step + (m.showBox[1] or 0) + (m.showBox[2] or 0) + (m.showBox[3] or 0)
						m.num = m.num + 1
						if bump > 0 and rowNum[RD.r + 2] then
							p.span = p.cols > c and (bump * 2) or math.max((bump - 1) / 2, 2)
							bumps = bumps + p.span
							for s = 2, p.span + 1 do
								if c == 1 and (RD.r + s) <= rows then
									newRow(RD.r + s)
								end
							end
							rowNum[RD.r + 2]:tag'td':attr{rowspan = p.span, colspan = 2}
						end
						r = r + 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 = (rows - rows / math.pow(2, c)) / math.pow(2, p.cols - c)
			bumps = p.unit + 1
			rowNum[1]
				:tag'td':attr{rowspan = bumps}
				:tag'td'
					:attr{rowspan = bump + 4}
					:css('border-bottom', not skipMatch[RD.top] and '1px solid' or nil)
			RD.n = 0
			for r = bumps + 1, rows, p.unit * 2 do
				p.tag = rowNum[r]:tag'td'
				if ((r - bumps - 1) / (p.unit * 2)) % 2 == 0 then
					--RD.t and RD.t2 control whether lines are drawn
					RD.t = RD.t2 or skipMatch[RD.tot + RD.n / 2 + 1] and 3 or ((skipMatch[RD.top] and 1 or 0) + (skipMatch[RD.top + 1] and 2 or 0))
					RD.n = RD.n + 2
					RD.t2 = skipMatch[RD.tot + RD.n / 2 + 1] and 3 or ((skipMatch[RD.top + RD.n] and 1 or 0) + (skipMatch[RD.top + RD.n + 1] and 2 or 0))
					if RD.t == 0 then
						p.tag
							:attr{rowspan = maxSpan(p.unit * 2, r, rows)}
							:css(skipMatch[RD.tot + RD.n / 2] and {} or {
								border = '3px solid',
								['border-left'] = 0
							})
					else
						p.tag
							:attr{rowspan = maxSpan(p.unit, r, rows)}
							:cssText(RD.t == 2 and 'border-width:3px 3px 0 0;border-style:solid' or nil)
						rowNum[r + p.unit]:tag'td'
							:attr{rowspan = maxSpan(p.unit, r + p.unit, rows)}
							:cssText(RD.t == 1 and 'border-width:0 3px 3px 0;border-style:solid' or nil)
					end
					rowNum[r + p.unit]:tag'td'
						:attr{rowspan = maxSpan(p.unit * 4, r + p.unit, rows)}
						:css((RD.t2 < 3 and rowNum[r + p.unit * 5] or RD.t < 3) and {
							['border-width'] = (RD.t < 3 and 2 or 0) .. 'px 0 ' .. (RD.t2 < 3 and rowNum[r + p.unit * 5] and '1px' or 0),
							['border-style'] = 'solid'
						} or {})
				else
					p.tag:attr{rowspan = maxSpan(p.unit * 2, r, rows)}
				end
			end
		end
	end
	if p['3rdplace'] or p.cols > 3 and p['3rdplace'] == nil then--begin 3rd place
		RD.r = rows / 2 + 4 + (p.span or 3)
		for r = rows + 1, RD.r + 7 do
			newRow(r)
		end
		if rowNum[rows + 1] and p.cols > 1 then
			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(args[step + 2], 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.scroll_height and
		mw.html.create'div'
			:cssText'border-bottom:1px solid #eee;display:inline-block'
			:node(not p.scroll_head_unlock and mw.html.create'div'
				:css{
					overflow = 'hidden',
					height = '3em',
					['border-bottom'] = 'inherit',
					['margin-right'] = '17px'
				}
				:node(mw.clone(output))
			)
			:tag'div'
				:cssText('overflow-y:scroll;max-height:'
					.. args.scroll_height .. (string.match(args.scroll_height, '^%d*$') and 'px' or '')
				)
				:node(not p.scroll_head_unlock and
					output:css('margin-top', '-3em')
					or output
				)
			:done()
		or output
end

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

return p