The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Documentation for this module may be created at Module:Election results/doc

require('Module:No globals')
local p = {}

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame)
	local index, headings, showtotal = {}, {}, {}
	local cols, rounds = 0, 1
	local winner, winner_votes = {0, 0}, {0, 0}
	local valid = {0, 0}
	local invalid = {tonumber(args.invalid) or 0, tonumber(args.invalid2) or 0}
	local majority = {tonumber(args.majority)}
	local majority2 = {tonumber(args.majority2)}
	local majoritypct = {tonumber(args.majoritypct)}
	local majoritypct2 = {tonumber(args.majoritypct2)}
	local totalvotes = {tonumber(args.totalvotes), tonumber(args.totalvotes2)}
	local electorate = {tonumber(args.electorate), tonumber(args.electorate2)}
	local turnout = {tonumber(args.turnout), tonumber(args.turnout2)}
	local seats = 0
	local seats_2 = 0
	local st1t = 0
	local st2t = 0
	local st3t = 0
	local st4t = 0
	local st5t = 0
	local totseats = 0
	local row, secondrow
	local tracking = ''
	local max_rows = 0

	-- helper functions
	local lang = mw.getContentLanguage()
	local function fmt(n)
		return n and tonumber(n) and lang:formatNum(tonumber(n)) or nil
	end
	local function pct(n, d)
		n, d = tonumber(n), tonumber(d)
		if n and d and d > 0 then
			return string.format('%.2f', n / d * 100)
		end
		return '–'
	end
	local function tonumdash(s)
		if s then
			s = mw.ustring.gsub(s, '&[MmNn][Dd][Aa][Ss][Hh];', '-')
			s = mw.ustring.gsub(s, '&[Mm][Ii][Nn][Uu][Ss];', '-')
			s = mw.ustring.gsub(s, '[—–−]', '-')
			return tonumber(s) or 0
		end
	end
	local function unlink(s)
		if s then
			s = s:match("^[^%[]-%[%[([^%]]-)|[^%]]-%]%].*$") or s
			s = s:match("^[^%[]-%[%[([^%]]-)%]%].*$") or s
		end
		return s
	end

	-- preprocess the input
	local stop_flag = false
	local i = 0
	local has_votes = args['invalid'] or args['totalvotes'] or args['electorate'] or args['turnout']
	local has_seats = args['seats' .. i .. '_2']
	local has_sw = args['sw' .. i .. '_2']
	local seats_only = has_seats and not has_votes
	local has_rspan = args['vspan' .. i]
	local has_sspan = args['sspan' .. i]
	while stop_flag == false do
		stop_flag = true
		for kk = 1, 20 do
			i = i + 1
			for k, key in ipairs({'alliance', 'atotal', 'cand', 'vp', 'party', 'ivotes', 'ipct', 'iseats', 'sc', 'st1t', 'st2t', 'st3t', 'st4t', 'st5t', 'sw', 'seats', 'totalvotes', 'totseats', 'acolor'}) do
				if args[key .. i] then
					headings[key] = true
					stop_flag = false
					max_rows = i > max_rows and i or max_rows
				end
			end
			if args['row' .. i] then
				stop_flag = false
				max_rows = i > max_rows and i or max_rows
			end

			if args['row' .. i] or args['cand' .. i] or args['party' .. i] or args['alliance' .. i] or args['atotal' .. i] or args['atotalnv' .. i] or args['header' .. i] then
				table.insert(index, i)
				if args['votes' .. i] then
					has_votes = true
					if tonumber(args['votes' .. i]) then showtotal.votes = 1 end
					local votesi = tonumber(args['votes' .. i]) or 0
					args['votes' .. i] = votesi
					if votesi > winner_votes[1] then
						winner[1] = i
						winner_votes[1] = votesi
					end
					valid[1] = valid[1] + votesi
				end
				if args['ivotes' .. i] then
					has_votes = true
				end
				if args['ipct' .. i] then
					has_votes = true
				end
				if args['sw' .. i .. '_2'] then
					has_sw = true
					rounds = 2
				end
				if args['valid2'] or args['invalid2'] or args['totalvotes2'] or args['electorate2'] or args['turnout2'] then
 					rounds = 2
				end
				if args['vspan' .. i] then
					has_rspan = true
				end
				if args['sspan' .. i] then
					has_sspan = true
				end
				if args['ipct' .. i .. '_2'] then
					rounds = 2
				end
				if args['votes' .. i .. '_2'] then
					has_votes = true
					rounds = 2
					if tonumber(args['votes' .. i .. '_2']) then showtotal.votes_2 = 1 end
					local votesi = tonumber(args['votes' .. i .. '_2']) or 0
					args['votes' .. i .. '_2'] = votesi
					if votesi > winner_votes[2] then
						winner[2] = i
						winner_votes[2] = votesi
					end
					valid[2] = valid[2] + votesi
				end
				if args['seats' .. i] then
					if tonumber(args['seats' .. i]) then showtotal.seats = 1 end
					seats = seats + (tonumber(args['seats' .. i]) or 0)
				end
				if args['seats' .. i .. '_2'] then
					has_seats = true
					rounds = 2
					if tonumber(args['seats' .. i ..'_2']) then showtotal.seats_2 = 1 end
					seats_2 = seats_2 + (tonumber(args['seats' .. i .. '_2']) or 0)
				end
				if args['st1t' .. i] then
					if tonumber(args['st1t' .. i]) then showtotal.st1t = 1 end
					st1t = st1t + (tonumber(args['st1t' .. i]) or 0)
				end
				if args['st2t' .. i] then
					if tonumber(args['st2t' .. i]) then showtotal.st2t = 1 end
					st2t = st2t + (tonumber(args['st2t' .. i]) or 0)
				end
				if args['st3t' .. i] then
					if tonumber(args['st3t' .. i]) then showtotal.st3t = 1 end
					st3t = st3t + (tonumber(args['st3t' .. i]) or 0)
				end
				if args['st4t' .. i] then
					if tonumber(args['st4t' .. i]) then showtotal.st4t = 1 end
					st4t = st4t + (tonumber(args['st4t' .. i]) or 0)
				end
				if args['st5t' .. i] then
					if tonumber(args['st5t' .. i]) then showtotal.st5t = 1 end
					st5t = st5t + (tonumber(args['st5t' .. i]) or 0)
				end
				if args['totseats' .. i] then
					if tonumber(args['totseats' .. i]) then showtotal.totseats = 1 end
					totseats = totseats + (tonumber(args['totseats' .. i]) or 0)
				end
				if has_seats and not has_votes then
					seats_only = true
				end
			end
		end
	end

	local ovalid = {valid[1], valid[2]}
	seats = ((args['total_seats'] or '') == 'TOTAL' and seats) or args['total_seats'] or seats
	totseats = ((args['total_totseats'] or '') == 'TOTAL' and totseats) or args['total_totseats'] or totseats
	st1t = ((args['total_st1t'] or '') == 'TOTAL' and st1t) or args['total_st1t'] or st1t
	st2t = ((args['total_st2t'] or '') == 'TOTAL' and st2t) or args['total_st2t'] or st2t
	st3t = ((args['total_st3t'] or '') == 'TOTAL' and st3t) or args['total_st3t'] or st3t
	st4t = ((args['total_st4t'] or '') == 'TOTAL' and st4t) or args['total_st4t'] or st4t
	st5t = ((args['total_st5t'] or '') == 'TOTAL' and st5t) or args['total_st5t'] or st5t
	if seats or totseats or st1t or st2t or st3t or st4t or st5t or args['total_sc'] or args['total_st1t'] or args['total_st2t'] or args['total_st3t'] or args['total_st4t'] or args['total_st5t'] or args['valid'] or ((rounds > 1) and args['valid2']) then
		max_rows = max_rows + 1
		local i = max_rows
		table.insert(index, i)
		args['votes' .. i] = showtotal.votes and valid[1] or nil
		args['votes' .. i .. '_2'] = showtotal.votes_2 and valid[2] or nil
		args['colour' .. i] = 'inherit'
		args['color' .. i] = 'inherit'
		args['row' .. i] = 'Total'
		args['sw' .. i] = '–'
		args['sw' .. i .. '_2'] = '–'
		args['seats' .. i] = showtotal.seats and seats or nil
		args['seats' .. i .. '_2'] = showtotal.seats_2 and seats_2 or nil
		args['st1t' .. i] = showtotal.st1t and st1t or args.total_st1t
		args['st2t' .. i] = showtotal.st2t and st2t or args.total_st2t
		args['st3t' .. i] = showtotal.st3t and st3t or args.total_st3t
		args['st4t' .. i] = showtotal.st4t and st4t or args.total_st4t
		args['st5t' .. i] = showtotal.st5t and st5t or args.total_st5t
		args['sc' .. i] = args['total_sc']
		args['totseats' .. i] = showtotal.totseats and totseats or nil
		args['font-weight' .. i] = 'bold'
		args['class' .. i] = 'sortbottom'
		ovalid[1] = tonumber(args['valid']) or valid[1]
		ovalid[2] = tonumber(args['valid2']) or valid[2]
	end

	-- build the table
	local root = mw.html.create(args['embed'] and '' or 'table')
	if args['embed'] == nil then
		root
			:addClass('wikitable sortable')
			:tag('caption')
				:wikitext(args.caption)
				:done()
	end

	local topcell = nil
	if args['image'] then
		topcell = root
			:tag('tr')
				:tag('td')
					:wikitext(args['image'])
					:css('text-align', 'center')
	end

	local rowspan = (rounds > 1) and 2 or headings['st1t'] and 2 or nil
	row = args['embed'] and mw.html.create('') or root:tag('tr')
	if args['dsv'] then
		row
			:tag('th')
				:wikitext('Party')
				:attr('scope', 'col')
				:attr('colspan', 2)
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 2
		row
			:tag('th')
			:wikitext(args.candtitle or 'Candidate')
			:attr('scope', 'col')
			:attr('rowspan', rowspan)
			:done()
		cols = cols + 1
	elseif headings['cand'] then
	if headings['cand'] and headings['party'] then
		row
			:tag('th')
				:wikitext('Candidate')
				:attr('scope', 'col')
				:attr('colspan', 2)
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 2
	end
	if headings['cand'] and not headings['party'] then
		row
			:tag('th')
				:wikitext('Candidate')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 1
	end
		if headings['vp'] then
			row
				:tag('th')
						:wikitext('Running mate')
						:attr('scope', 'col')
						:attr('rowspan', rowspan)
						:done()
			cols = cols + 1
		end
		if headings['party'] then
			row
				:tag('th')
				:wikitext('Party')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
			cols = cols + 1
		end
	elseif headings['party'] and headings['alliance'] then
			row
				:tag('th')
				:wikitext(args.partytitle or 'Party or alliance')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:attr('colspan', 4)
				:done()
			cols = cols + 4
	else 
			row
			:tag('th')
				:wikitext(args.partytitle or 'Party')
				:attr('scope', 'col')
				:attr('colspan', 2)
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 2
	end
	if rounds > 1 then
		if has_seats and headings['sw'] then
			row
				:tag('th')
					:wikitext(args.firstround or 'First round')
					:attr('scope', 'col')
					:attr('colspan', 4)
					:done()
				:tag('th')
					:wikitext(args.secondround or 'Second round')
					:attr('scope', 'col')
					:attr('colspan', 4)
					:done()
		elseif has_seats then
			row
				:tag('th')
					:wikitext(args.firstround or 'First round')
					:attr('scope', 'col')
					:attr('colspan', 3)
					:done()
				:tag('th')
					:wikitext(args.secondround or 'Second round')
					:attr('scope', 'col')
					:attr('colspan', 3)
					:done()
		elseif has_votes then
			row
				:tag('th')
					:wikitext(args.firstround or 'First round')
					:attr('scope', 'col')
					:attr('colspan', 2)
					:done()
				:tag('th')
					:wikitext(args.secondround or 'Second round')
					:attr('scope', 'col')
					:attr('colspan', 2)
					:done()
		end
		secondrow = args['embed'] and mw.html.create('') or root:tag('tr')
	else
		secondrow = row
	end
	rowspan = (rounds > 1) and 1 or (rounds < 2 and headings['st1t']) and 2 or nil
	if has_seats and headings['sw'] then
		for k=1, rounds do
			secondrow
				:tag('th')
					:wikitext('Votes')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('%')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('+/–')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('Seats')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
			cols = cols + 4
		end
	end
	if has_seats and not headings['sw'] then
		for k=1, rounds do
			secondrow
				:tag('th')
					:wikitext('Votes')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('%')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('Seats')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
			cols = cols + 3
		end
	end
	if has_votes and not has_seats then
		for k=1, rounds do
			secondrow
				:tag('th')
					:wikitext('Votes')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('%')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
			cols = cols + 2
		end
	end
	if headings['sw'] and not has_seats then
		row
			:tag('th')
				:wikitext('+/–')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 1
	end
	rowspan = (rounds > 1) and 2 or headings['sc'] and headings['st1t'] and 2 or nil
	if headings['seats'] and not has_seats then
		row
			:tag('th')
				:wikitext('Seats')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 1
	end
	if headings['st1t'] then
		row
			:tag('th')
				:wikitext('Seats')
				:attr('colspan', 5)
				:attr('scope', 'col')
				:done()
	if rounds < 2 then secondrow = root:tag('tr')
	end
		secondrow
			:tag('th')
				:wikitext(args.seattype1)
				:attr('scope', 'col')
				:done()
		cols = cols + 1
	if headings['st2t'] then
		secondrow
			:tag('th')
				:wikitext(args.seattype2)
				:attr('scope', 'col')
				:done()
		cols = cols + 1
	end
	if headings['st3t'] then
		secondrow
			:tag('th')
				:wikitext(args.seattype3)
				:attr('scope', 'col')
				:done()
		cols = cols + 1
	end
	if headings['st4t'] then
		secondrow
			:tag('th')
				:wikitext(args.seattype4)
				:attr('scope', 'col')
				:done()
		cols = cols + 1
	end
	if headings['st5t'] then
		secondrow
			:tag('th')
				:wikitext(args.seattype5)
				:attr('scope', 'col')
				:done()
		cols = cols + 1
	end
	end
	if headings['totseats'] then
		row
			:tag('th')
				:wikitext(headings['totseats'] and 'Total<br>seats' or 'Totseats&pm;')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 1
	end
	if headings['sc'] then
		row
			:tag('th')
				:wikitext('+/–')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 1
	end
	if topcell then
		topcell:attr('colspan', cols)
	end

	local cs = cols - 2*rounds
			- (headings['sw'] and 1 or 0)
			- (headings['totseats'] and 1 or 0)
			- (headings['seats'] and 1 or 0)
			- (headings['sc'] and 1 or 0)
			- (headings['st1t'] and 1 or 0)
			- (headings['st2t'] and 1 or 0)
			- (headings['st3t'] and 1 or 0)
			- (headings['st4t'] and 1 or 0)
			- (headings['st5t'] and 1 or 0)
			- (has_seats and 1 or 0)
			- (has_sw and 1 or 0)
			+ (has_votes and 0 or 2)
			- (seats_only and 2 or 0)
	local rsuff = (rounds > 1) and {'', '_2'} or {''}
	for i, v in ipairs(index) do
		row = root:tag('tr')
			:addClass(args['class' .. v])
			:css('font-weight', args['font-weight' .. v])

		-- determine the color
		local color = args['colour' .. v] or args['color' .. v] or nil
		if color == nil then
			local party = unlink(args['party' .. v]) or ''
			if party ~= '' and mw.title.new('Template:' .. party .. '/meta/color').exists then
				color = frame:expandTemplate{title = party .. '/meta/color'}
			end
		end
		local acolor = args['acolour' .. v] or args['acolor' .. v] or nil
		if acolor == nil then
			local alliance = unlink(args['alliance' .. v]) or ''
			if alliance ~= '' and mw.title.new('Template:' .. alliance .. '/meta/color').exists then
				acolor = frame:expandTemplate{title = alliance .. '/meta/color'}
			end
		end
		if args['header' .. v] then
			row
			:tag('th')
				:wikitext(args['header' .. v])
				:attr('colspan', cols)
		else
		if args['row' .. v] or headings['cand'] and not headings['party'] then
			row
				:css('background-color', args['acolor' .. v])
				:tag('td')
					:wikitext(args['row' .. v] or args['cand' .. v])
					:attr('colspan', cs)
		elseif args['alliance' .. v] and args['party' .. v] then
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:css('background-color', acolor)
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:wikitext(args['alliance' .. v])
			row
				:tag('td')
					:css('background-color', color)
			row
				:tag('td')
					:wikitext(args['party' .. v])
		elseif args['alliance' .. v] then
			row
				:tag('td')
					:css('background-color', acolor)
			row
				:tag('td')
					:attr('colspan', '3')
					:wikitext(args['alliance' .. v])
		elseif args['atotal' .. v] or args['atotalnv' .. v] then
				row
					:css('font-style', 'italic')
					:tag('td')
						:attr('colspan', '2')
						:wikitext('Total')
		else
			-- create the empty color cell
			row
				:css('background-color', args['acolor' .. v])
				:tag('td')
					:css('background-color', color)
			-- add the rest of the row
			if args['dsv'] then
				row
					:tag('td')
						:wikitext(args['party' .. v])
				row
					:tag('td')
						:wikitext(args['cand' .. v])
			end
			if headings['cand'] and not args['dsv'] then
				row
					:tag('td')
						:wikitext(args['cand' .. v])
				if headings['vp'] then
					row
						:tag('td')
							:wikitext(args['vp' .. v])
				end
			end
			if headings['party'] and not args['dsv'] then
				row
					:tag('td')
						:wikitext(args['party' .. v])
			end
		end
		if args['votes' .. v] then
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['votes' .. v]))
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:wikitext(pct(args['votes' .. v], valid[1]))
		elseif args['ivotes' .. v] or args ['ipct' .. v] or args['atotal' .. v] then
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', 'italic')
					:wikitext(fmt(args['ivotes' .. v]) or fmt(args['atotal' .. v]))
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', 'italic')
					:wikitext(args['ipct' .. v] or args['apct' .. v])
		elseif has_votes and not headings['sw'] and not has_seats and not has_rspan or args['invalid'] and not headings['sw'] and not has_seats and not has_rspan or args['totalvotes'] and not headings['sw'] and not has_seats and not has_rspan or args['electorate'] and not headings['sw'] and not has_seats and not has_rspan or args['turnout'] and not headings['sw'] and not has_seats and not has_rspan or has_seats and args['seats' .. v] and not args['votes' .. v] and not args['ivotes' .. v] and not has_rspan or has_votes and not args['votes' .. v] and not args['ivotes' .. v] and not args['ipct' .. v] and not args['apct' .. v] and not args['atotal' .. v]  and args['row' .. v] and headings ['alliance'] and not headings['sw'] or args['atotalnv' .. v] and args['aseats' .. v] then
				row:tag('td')
					:attr('colspan', 2)
		elseif has_seats and not args['seats' .. v] and not headings['sw'] or has_votes and headings['sw'] and not has_seats then
				row:tag('td')
					:attr('colspan', 3)
		elseif has_seats and headings['sw'] then
				row:tag('td')
					:attr('colspan', 4)
		end
		if headings['sw'] and args['votes' .. v] or headings['sw'] and args['ivotes' .. v] or headings['sw'] and args['atotal' .. v] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['sw' .. v])
		end
		if headings['seats'] and not args['dsv'] and not has_seats and rounds < 2 or has_seats and args['seats' .. v] or has_seats and args['votes' .. v] or has_seats and args['iseats' .. v] or has_seats and args['aseats' .. v] then
		if args['iseats' .. v] or args['aseats' .. v] then
			row
				:tag('td')
					:css('font-style', 'italic')
					:css('text-align', 'right')
					:wikitext(args['iseats' .. v] or args['aseats' .. v])
		elseif args['seats' .. v] or headings['seats'] and not has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['seats' .. v]) or '–')
		end
		end
		if args['votes' .. v .. '_2'] then
				row:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['votes' .. v .. '_2']))
				row:tag('td')
					:css('text-align', 'right')
					:wikitext(pct(args['votes' .. v .. '_2'], valid[2]))
		elseif args['ivotes' .. v .. '_2'] or args ['ipct' .. v.. '_2'] or args['avotes' .. v .. '_2'] then
				row:tag('td')
					:css('text-align', 'right')
					:css('font-style', 'italic')
					:wikitext(fmt(args['ivotes' .. v .. '_2']) or fmt(args['avotes' .. v .. '_2']))
				row:tag('td')
					:css('text-align', 'right')
					:css('font-style', 'italic')
					:wikitext(args['ipct' .. v .. '_2'] or args['apct' .. v .. '_2'])
		elseif args['invalid2'] and args['seats' .. v .. '_2'] or args['totalvotes2'] and args['seats' .. v .. '_2'] or args['electorate2'] and args['seats' .. v .. '_2'] or args['turnout2'] and args['seats' .. v .. '_2'] or has_votes and rounds > 1 and args['seats' .. v .. '_2'] or has_votes and rounds > 1 and not has_seats or has_seats and not args['votes' .. v .. '_2'] and args['seats' .. v .. '_2'] or args['atotalnv' .. v] and args['aseats' .. v .. '_2'] then
				row:tag('td')
					:attr('colspan', 2)
		elseif has_seats and not args['seats' .. v .. '_2'] and not headings['sw'] then
				row:tag('td')
					:attr('colspan', 3)
		elseif has_seats and headings['sw'] then
				row:tag('td')
					:attr('colspan', 4)
		end
		if headings['seats'] and args['dsv'] or headings['seats'] and rounds > 1 and not has_seats and not has_sspan or args['seats' .. v] and has_sspan and rounds > 1 then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['seats' .. v]) or '–')
		end
		if has_sw and args['votes' .. v .. '_2'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['sw' .. v .. '_2'])
		end
		if args['seats' .. v .. '_2'] or has_seats and args['votes' .. v .. '_2'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['seats' .. v .. '_2']) or '–')
		end
		if args['iseats' .. v .. '_2'] or has_seats and args['ivotes' .. v .. '_2'] or args['aseats' .. v .. '_2'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['iseats' .. v .. '_2']) or fmt(args['aseats' .. v .. '_2']))
		end
		if headings['st1t'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['st1t' .. v] or '–')
		end
		if headings['st2t'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['st2t' .. v] or '–')
		end
		if headings['st3t'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['st3t' .. v] or '–')
		end
		if headings['st4t'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['st4t' .. v] or '–')
		end
		if headings['st5t'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['st5t' .. v] or '–')
		end
		if headings['totseats'] and not args['itotseats' .. v] and not args['atotseats' .. v] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['totseats' .. v]) or '–')
		elseif headings['totseats'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:css('font-style', 'italic')
					:wikitext(args['itotseats' .. v] or args['atotseats' .. v])
		end
		if headings['sc'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(args['sc' .. v] or '–')
		end
		end
	end
	-- separating line
	row = root
		:tag('tr')
			:addClass('sortbottom')
	row
		:tag('td')
			:css('background', '#eaecf0')
			:attr('colspan', cols)
	-- valid votes
	if args['invalid'] or args['invalid2'] then
	row = root
		:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
	row
		:tag('th')
			:wikitext('Valid votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('background', 'inherit')
	if args['invalid'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[1]))
		:tag('td')
				:wikitext(pct(ovalid[1], ovalid[1] + invalid[1]))
	elseif not args['invalid'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and args['validsw'] then
	row
		:tag('td')
			:wikitext(args['validsw'])
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['validsw'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if rounds > 1 and args['invalid2'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[2]))
		:tag('td')
				:wikitext(pct(ovalid[2], ovalid[2] + invalid[2]))
	elseif rounds > 1 and not args['invalid2'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if has_seats and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and args['validsw2'] then
	row
		:tag('td')
			:wikitext(args['validsw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['validsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if args['invalidsw'] and not has_sw then
		row:tag('td')
			:wikitext(args['validsw'])
		local cspan = (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
	if headings['seats'] or headings['sc'] then
		local cspan = (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	elseif headings['sw'] or headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['sw'] and 1 or 0) + (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	-- invalid votes
	row = root:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
		if args['blank'] then
	row
		:tag('th')
			:wikitext('Invalid votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('background', 'inherit')
	else
	row
		:tag('th')
			:wikitext('Invalid/blank votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('background', 'inherit')
	end
	if args['invalid'] then
	row
		:tag('td')
			:wikitext(fmt(invalid[1]))
		:tag('td')
				:wikitext(pct(invalid[1], ovalid[1] + invalid[1]))
	elseif not args['invalid'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and args['invalidsw'] then
	row
		:tag('td')
			:wikitext(args['invalidsw'])
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['invalidsw'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if rounds > 1 and args['invalid2'] then
	row
		:tag('td')
			:wikitext(fmt(invalid[2]))
		:tag('td')
				:wikitext(pct(invalid[2], ovalid[2] + invalid[2]))
	elseif rounds > 1 and not args['invalid2'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and args['invalidsw2'] then
	row
		:tag('td')
			:wikitext(args['invalidsw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['invalidsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if args['invalidsw'] and not has_sw then
		row:tag('td')
			:wikitext(args['invalidsw'])
	if headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	elseif headings['sw'] or headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['sw'] and 1 or 0) + (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	end
	-- total
	if args['invalid'] or args['totalvotes'] or args['invalid2'] or args['totalvotes2'] then
	row = root:tag('tr')
			:addClass('sortbottom')
			:css('font-weight', 'bold')
			:css('text-align', 'right')
	row
		:tag('th')
			:wikitext('Total votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('background', 'inherit')
	if not args['totalvotes'] and args['invalid'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[1] + invalid[1]))
		:tag('td')
			:wikitext(pct(1, 1))
	elseif args['totalvotes'] then
	row
		:tag('td')
			:wikitext(fmt(args.totalvotes))
		:tag('td')
			:wikitext('–')
	elseif not args['invalid'] and not args['totalvotes'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if has_seats and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and args['invalidsw'] then
	row
		:tag('td')
			:wikitext('–')
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['invalidsw'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if rounds > 1 and args['invalid2'] and not args['totalvotes2'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[2] + invalid[2]))
		:tag('td')
			:wikitext(pct(1, 1))
	elseif rounds > 1 and args['totalvotes2'] then
	row
		:tag('td')
			:wikitext(fmt(args.totalvotes2))
		:tag('td')
			:wikitext('–')
	elseif rounds > 1 and not args['invalid2'] or rounds > 1 and not args['totalvotes2'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and args['invalidsw2'] then
	row
		:tag('td')
			:wikitext('–')
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['invalidsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if args['invalidsw'] and not has_sw then
		row:tag('td')
			:wikitext('–')
	if headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	elseif headings['sw'] or headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['sw'] and 1 or 0) + (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	end
	-- registered
	if args['electorate'] or args['electorate2'] or args['turnout'] or args['turnout2'] then
	row = root:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
	row
		:tag('th')
			:wikitext('Registered voters/turnout')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('background', 'inherit')
	row
			:tag('td')
				:wikitext(fmt(electorate[1]))
	if args['invalid'] and args['electorate'] then
		row
			:tag('td')
				:wikitext(pct(ovalid[1] + invalid[1], electorate[1]))
	elseif args['totalvotes'] and args['electorate'] then
		row
			:tag('td')
				:wikitext(pct(totalvotes[1], electorate[1]))
	elseif args['electorate'] then
		row
			:tag('td')
				:wikitext(args.turnout or '–')
	else
		row
			:tag('td')
				:wikitext(args.turnout)
	end
	if has_seats and not headings['sw'] then
		row
			:tag('td')
				:wikitext()
	end
	if has_seats and headings['sw'] and args['turnoutsw'] then
	row
		:tag('td')
			:wikitext(args['turnoutsw'])
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['turnoutsw'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if rounds > 1 then
		row
			:tag('td')
				:wikitext(fmt(electorate[2]))
	if args['invalid2'] and args['electorate2'] then
		row
			:tag('td')
				:wikitext(pct(ovalid[2] + invalid[2], electorate[2]))
	elseif args['totalvotes2'] and args['electorate2'] then
		row
			:tag('td')
				:wikitext(pct(totalvotes[2], electorate[2]))
	elseif args['electorate2'] then
		row
			:tag('td')
				:wikitext(args.turnout2 or '–')
	else
		row
			:tag('td')
				:wikitext(args.turnout2)
	end
	if has_seats and not headings['sw'] then
		row
			:tag('td')
				:wikitext()
	end
	if has_seats and headings['sw'] and args['turnoutsw2'] then
	row
		:tag('td')
			:wikitext(args['turnoutsw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats and headings['sw'] and not args['turnoutsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	end
	if args['turnoutsw'] and not has_sw then
		row:tag('td')
			:wikitext(args['turnoutsw'])
	if headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	elseif headings['sw'] or headings['seats'] or headings['sc'] or headings['st1t'] or headings['st2t'] or headings['st3t'] or headings['st4t'] or headings['st5t'] then
		local cspan = (headings['sw'] and 1 or 0) + (headings['seats'] and 1 or 0) + (headings['sc'] and 1 or 0) + (headings['st1t'] and 1 or 0) + (headings['st2t'] and 1 or 0) + (headings['st3t'] and 1 or 0) + (headings['st4t'] and 1 or 0) + (headings['st5t'] and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	end
	if args['majority'] then
	if args['invalid'] or args['electorate'] then
	row = root
		:tag('tr')
			:addClass('sortbottom')
	row
		:tag('td')
			:css('background', '#eaecf0')
			:attr('colspan', cols)
	end
	row = root
		:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
	row
		:tag('th')
			:wikitext('Majority')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('background', 'inherit')
	for k=1, rounds do
	row
		:tag('td')
			:wikitext(fmt(majority[k]))
		:tag('td')
			:wikitext(fmt(majoritypct[k]))
	end
	if args['majoritysw'] then
	row
		:tag('td')
			:wikitext(args['majoritysw'])
	end
	end
	if args['result'] then
	row = root:tag('tr')
			:addClass('sortbottom')
		-- determine the color
		local color = args['resultcolour'] or nil
		if color == nil then
			local result = unlink(args['result']) or ''
			if result ~= '' and mw.title.new('Template:' .. result .. '/meta/color').exists then
				color = frame:expandTemplate{title = result .. '/meta/color'}
			end
			end
	if args['resultsw'] then
		row
			:tag('td')
				:css('background-color', color)
		row
			:tag('td')
				:attr('colspan', 2)
				:wikitext(args['result'])
		row
			:tag('td')
				:attr('colspan', 2)
				:css('text-align', 'right')
				:wikitext('Swing')
		row
			:tag('td')
				:css('text-align', 'right')
				:wikitext(args['resultsw'])
	else
		row
			:tag('td')
				:css('background-color', color)
		row
			:tag('td')
				:attr('colspan', cols - 1)
				:wikitext(args['result'])
	end
	end
	if args['majority2'] then
	row = root
		:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
	row
		:tag('th')
			:wikitext('Majority')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('background', 'inherit')
	for k=1, rounds do
	row
		:tag('td')
			:wikitext(fmt(majority2[k]))
		:tag('td')
			:wikitext(fmt(majoritypct2[k]))
	end
	if args['majoritysw2'] then
	row
		:tag('td')
			:wikitext(args['majoritysw2'])
	end
	end
	if args['result2'] then
	row = root:tag('tr')
			:addClass('sortbottom')
		-- determine the color
		local color = args['resultcolour'] or nil
		if color == nil then
			local result = unlink(args['result2']) or ''
			if result ~= '' and mw.title.new('Template:' .. result .. '/meta/color').exists then
				color = frame:expandTemplate{title = result .. '/meta/color'}
			end
			end
	if args['resultsw2'] then
		row
			:tag('td')
				:css('background-color', color)
		row
			:tag('td')
				:attr('colspan', 2)
				:wikitext(args['result2'])
		row
			:tag('td')
				:attr('colspan', 2)
				:css('text-align', 'right')
				:wikitext('Swing')
		row
			:tag('td')
				:css('text-align', 'right')
				:wikitext(args['resultsw2'])
	else
		row
			:tag('td')
				:css('background-color', color)
		row
			:tag('td')
				:attr('colspan', cols - 1)
				:wikitext(args['result2'])
	end
	end
	if args['source'] then
	row = root:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
	row:tag('td')
		:wikitext('Source: ', args.source)
		:attr('colspan', cols)
		:css('text-align', 'left')
	end
	if args['embedded'] then
		root:wikitext(args['embedded'])
	end
	return tostring(root) .. tracking
end

return p