Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Module:Election results

Permanently protected module
From Wikipedia, the free encyclopedia

require('strict')
local p = {}
local political_party = require('Module:Political party')

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, 0, 0}
	local valid = {0, 0, 0}
	local invalid = {tonumber(args.invalid) or 0, tonumber(args.invalid2) or 0, tonumber(args.invalid3) or 0}
	local blank = {tonumber(args.blank) or 0, tonumber(args.blank2) or 0, tonumber(args.blank3) 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), tonumber(args.totalvotes3)}
	local electorate = {tonumber(args.electorate), tonumber(args.electorate2), tonumber(args.electorate3)}
	local turnout = {tonumber(args.turnout), tonumber(args.turnout2), tonumber(args.turnout3)}
	local seats = 0
	local seats_2 = 0
	local seats_3 = 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
	local function get_color(color, party)
		if color == nil then
			local party = unlink(party) or ''
			if party ~= '' then
				color = political_party._fetch({party, 'color'})
			end
		end

		if color ~= nil then
			color = mw.ustring.gsub(color, '&(#)35;', '%1')
		end
		return color
	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_votes2 = args['invalid2'] or args['totalvotes2'] or args['electorate2'] or args['turnout2']
	local has_votes3 = args['votes' .. i .. '_3'] or args['ivotes' .. i .. '_3']
	local has_seats2 = args['seats' .. i .. '_2'] or args['iseats' .. i .. '_2']
	local has_seats3 = args['seats' .. i .. '_3']
	local has_sw2 = args['sw' .. i .. '_2']
	local seats_only = has_seats2 and not has_votes
	local has_cand = args['cand' .. i]
	local has_alliance = args['alliance' .. i]
	local has_party = args['party' .. i]
	local has_rspan1 = args['vspan' .. i]
	local has_rspan2 = args['v2span' .. i]
	local has_rspan3 = args['v3span' .. i]
	local has_sspan = args['sspan' .. i] or args['s2span' .. i] or args['tsspan' .. 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', 'rcolor'}) 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['votes' .. i .. '_2'] or args['ivotes' .. i .. '_2'] or args['avotes' .. i .. '_2'] or args['ipct' .. i .. '_2'] or args['apct' .. i .. '_2'] then
					has_votes2 = true
				end
				if args['votes' .. i .. '_3'] or args['ivotes' .. i .. '_3'] or args['ipct' .. i .. '_3'] then
					has_votes3 = true
				end
				if args['alliance' .. i] then
					has_alliance = true
				end
				if args['party' .. i] then
					has_party = true
				end
				if args['cand' .. i] then
					has_cand = true
				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_sw2 = true
					rounds = (rounds < 2) and 2 or rounds
				end
				if args['valid2'] or args['invalid2'] or args['totalvotes2'] or args['electorate2'] or args['turnout2'] then
 					rounds = (rounds < 2) and 2 or rounds
				end
				if args['vspan' .. i] then
					has_rspan1 = true
				end
				if args['v2span' .. i] then
					has_rspan2 = true
				end
				if args['v3span' .. i] then
					has_rspan3 = true
				end
				if args['sspan' .. i] or args['s2span' .. i] or args['tsspan' .. i] then
					has_sspan = true
				end
				if args['ipct' .. i .. '_2'] then
					rounds = (rounds < 2) and 2 or rounds
				end
				if args['votes' .. i .. '_2'] then
					has_votes = true
					rounds = (rounds < 2) and 2 or rounds
					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['votes' .. i .. '_3'] then
					has_votes = true
					rounds = (rounds < 3) and 3 or rounds
					if tonumber(args['votes' .. i .. '_3']) then showtotal.votes_3 = 1 end
					local votesi = tonumber(args['votes' .. i .. '_3']) or 0
					args['votes' .. i .. '_3'] = votesi
					if votesi > winner_votes[3] then
						winner[3] = i
						winner_votes[3] = votesi
					end
					valid[3] = valid[3] + 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'] or args['iseats' .. i .. '_2'] then
					has_seats2 = true
					rounds = (rounds < 2) and 2 or rounds
					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['seats' .. i .. '_3'] then
					has_seats3 = true
					rounds = (rounds < 3) and 3 or rounds
					if tonumber(args['seats' .. i ..'_3']) then showtotal.seats_3 = 1 end
					seats_3 = seats_3 + (tonumber(args['seats' .. i .. '_3']) 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_seats2 and not has_votes or has_seats3 and not has_votes then
					seats_only = true
				end
			end
		end
	end

	-- add majority needed row above total
	if args.majorityneeded then
		max_rows = max_rows + 1
		local i = max_rows
		table.insert(index, i)
		args['row' .. i] = 'Required majority'
		args['ivotes' .. i] = '_NO_CELL_'
		args['ipct' .. i] = args['majorityneeded']
		args['rcolor' .. i] = '#f2f2f2'
		args['noitalic' .. i] = true
	end

	-- add total row at the bottom
	local ovalid = {valid[1], valid[2], valid[3]}
	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']) or ((rounds > 1) and args['valid3']) 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['votes' .. i .. '_3'] = showtotal.votes_3 and valid[3] 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['seats' .. i .. '_3'] = showtotal.seats_3 and seats_3 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]
		ovalid[3] = tonumber(args['valid3']) or valid[3]
	end

	-- build the table
	local root = mw.html.create(args['embed'] and '' or 'table')
	if args['embed'] == nil and args['floatright'] then
		root
			:addClass('wikitable sortable floatright')
			:tag('caption')
				:wikitext(args.caption)
				:done()
	elseif args['embedded'] then
		root
			:addClass('wikitable')
			:tag('caption')
				:wikitext(args.caption)
				:done()
	elseif 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('th')
				:css('text-align', 'center')
				:css('color', 'var( --color-base, #202122 )')
				:css('background', 'var( --background-color-neutral, #F8F9FA )')
				:wikitext(args['image'])
	end
	if args['reporting'] then
		if (topcell == nil) then
			topcell = root
				:tag('td')
					:css('text-align', 'center')
					:css('color', 'var( --color-base, #202122 )')
					:css('background', 'var( --background-color-neutral, #F8F9FA )')
		end
		topcell:tag('div')
				:addClass('center')
				:wikitext(frame:expandTemplate{
					title = 'percentage bar',
					args = {args.reporting, 
						args.reporting .. '% reporting',
						['width'] = '200px',
						['hex'] = '1BCE0E'
						}})
	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'] and has_alliance and has_party then
		row
			:tag('th')
				:wikitext(args.partytitle or 'Party or alliance')
				:attr('scope', 'col')
				:attr('colspan', 4)
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 4
		row
			:tag('th')
			:wikitext(args.candtitle or 'Candidate')
			:attr('scope', 'col')
			:attr('rowspan', rowspan)
			:done()
		cols = cols + 1
	elseif args['dsv'] then
		row
			:tag('th')
				:wikitext(args.partytitle or '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 has_cand then
	if has_cand and has_party and has_alliance then
			row
				:tag('th')
				:wikitext('Candidate')
				:attr('rowspan', rowspan)
				:attr('colspan', 2)
		if headings['vp'] then
			row
				:tag('th')
						:wikitext('Running mate')
						:attr('scope', 'col')
						:attr('rowspan', rowspan)
						:done()
			cols = cols + 1
		end
			row
				:tag('th')
				:wikitext(args.partytitle or 'Party or alliance')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:attr('colspan', 3)
				:done()
			cols = cols + 5
	elseif has_cand and has_party then
		row
			:tag('th')
				:wikitext('Candidate')
				:attr('scope', 'col')
				:attr('colspan', 2)
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 2
	end
	if has_cand and not has_party then
		row
			:tag('th')
				:wikitext('Candidate')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
		cols = cols + 1
	end
		if headings['vp'] and not has_alliance then
			row
				:tag('th')
						:wikitext('Running mate')
						:attr('scope', 'col')
						:attr('rowspan', rowspan)
						:done()
			cols = cols + 1
		end
		if has_party and not has_alliance then
			row
				:tag('th')
				:wikitext(args.partytitle or 'Party')
				:attr('scope', 'col')
				:attr('rowspan', rowspan)
				:done()
			cols = cols + 1
		end
	elseif has_party and has_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_seats2 and headings['sw'] and not has_seats3 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_seats2 and not has_seats3 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_seats3 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()
				:tag('th')
					:wikitext(args.thirdround or 'Third round')
					:attr('scope', 'col')
					:attr('colspan', 3)
					:done()
		elseif has_votes3 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()
				:tag('th')
					:wikitext(args.thirdround or 'Third round')
					:attr('scope', 'col')
					:attr('colspan', 2)
					: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_seats2 and not has_seats3 and headings['sw'] then
		for k=1, rounds do
			secondrow
				:tag('th')
					:wikitext(args.votestitle or '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(args.seatstitle or 'Seats')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
			cols = cols + 4
		end
	end
	if has_seats2 and not headings['sw'] or has_seats3 and not headings['sw'] then
		for k=1, rounds do
			secondrow
				:tag('th')
					:wikitext(args.votestitle or 'Votes')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext('%')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
				:tag('th')
					:wikitext(args.seatstitle or 'Seats')
					:attr('scope', 'col')
					:attr('rowspan', rowspan)
					:done()
			cols = cols + 3
		end
	end
	if has_votes and not has_seats2 then
		for k=1, rounds do
			secondrow
				:tag('th')
					:wikitext(args.votestitle or '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_seats2 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 headings['seats'] and headings['st1t'] and 2 or nil
	if headings['seats'] and not has_seats2 then
		row
			:tag('th')
				:wikitext(args.seatstitle or '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(args.sctitle or '+/–')
				: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_seats2 and 1 or 0)
			- (has_seats3 and 1 or 0)
			- (has_sw2 and 1 or 0)
			+ (has_votes and 0 or 2)
			- (seats_only and 2 or 0)
	local rsuff = (rounds > 1) and {'', '_2'} or (rounds > 2) and {'', '_3'} or {''}
	for i, v in ipairs(index) do
		local has_votesrow = args['votes' .. v] or args['ivotes' .. v] or args['avotes' .. v] or args['ipct' .. v] or args['apct' .. v] or args['atotal' .. v]
		local has_seatsrow = args['seats' .. v] or args['iseats' .. v] or args['aseats' .. v]
		local has_votesrow2 = args['votes' .. v .. '_2'] or args['ivotes' .. v .. '_2'] or args['avotes' .. v .. '_2'] or args['ipct' .. v .. '_2'] or args['apct' .. v .. '_2'] or args['atotal' .. v .. '_2']
		local has_seatsrow2 = args['seats' .. v .. '_2'] or args['iseats' .. v .. '_2'] or args['aseats' .. v .. '_2']
		local has_votesrow3 = args['votes' .. v .. '_3'] or args['ivotes' .. v .. '_3'] or args['avotes' .. v .. '_3'] or args['ipct' .. v .. '_3'] or args['apct' .. v .. '_3'] or args['atotal' .. v .. '_3']
		local has_seatsrow3 = args['seats' .. v .. '_3'] or args['iseats' .. v .. '_3'] or args['aseats' .. v .. '_3']
		row = root:tag('tr')
			:addClass(args['class' .. v])
			:css('font-weight', args['font-weight' .. v])

		-- determine the colors
		local color = get_color(args['colour' .. v] or args['color' .. v] or nil, args['party' .. v])
		local acolor = get_color(args['acolour' .. v] or args['acolor' .. v] or nil, args['alliance' .. v])
		local rcolor = get_color(args['rcolour' .. v] or args['rcolor' .. v] or nil)
		local italic_style = args['noitalic' .. v] and '' or 'italic'

		-- headers
		if args['header' .. v] then
			row
			:tag('th')
				:wikitext(args['header' .. v])
				:attr('colspan', cols)
		else
		if args['alliance' .. v] then
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:css('width', '0px')
					:css('background-color', acolor)
		elseif args['party' .. v] and not has_alliance or args['cand' .. v] and has_party and not has_alliance then
			row
				:tag('td')
					:css('width', '0px')
					:css('background-color', color)
		end
		if args['cand' .. v] and not args.dsv then
			row
					:css('background-color', rcolor)
				:tag('td')
					:attr('rowspan', args['candspan' .. v] or args['aspan' .. v])
					:wikitext(args['cand' .. v])
		end
		if args['vp' .. v] then
			row
				:tag('td')
					:attr('rowspan', args['vpspan' .. v] or args['candspan' .. v] or args['aspan' .. v])
					:wikitext(args['vp' .. v])
		end
		if args['alliance' .. v] and not has_party and args.dsv then
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:wikitext(args['alliance' .. v])
		end
		if args['alliance' .. v] and not args['party' .. v] and not args.dsv then
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:attr('colspan', 3)
					:wikitext(args['alliance' .. v])
		end
		if args['alliance' .. v] and not args['party' .. v] and has_party and args.dsv then
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:attr('colspan', 3)
					:wikitext(args['alliance' .. v])
		end
		if args['alliance' .. v] and args['party' .. v] then
			row
				:tag('td')
					:attr('rowspan', args['aspan' .. v])
					:wikitext(args['alliance' .. v])
		end
		if args['party' .. v] and not has_alliance or args['cand' .. v] and has_party and not has_alliance then
			row
				:tag('td')
					:wikitext(args['party' .. v])
		end
		if args['row' .. v] then
			local extracs = 0 + ((args['ivotes' .. v] or args['atotal' .. i] or '') == '_NO_CELL_' and 1 or 0)
			row
					:css('background-color', rcolor)
				:tag('td')
					:attr('colspan', cs + extracs)
					:wikitext(args['row' .. v])
		end
		if has_alliance and args['party' .. v] then
			row
				:tag('td')
					:css('background-color', color)
				:tag('td')
					:wikitext(args['party' .. v])
		end
		if args['cand' .. v] and args.dsv then
			row
				:tag('td')
					:attr('rowspan', args['candspan' .. v])
					:wikitext(args['cand' .. v])
		end
		if args['atotal' .. v] and has_party and not args.dsv or args['atotalnv' .. v] and has_party and not args.dsv then
			row
					:css('font-style', italic_style)
				:tag('td')
					:attr('colspan', 2)
					:wikitext('Total')
		end
		if args['atotal' .. v] and not has_party or args['atotalnv' .. v] and not has_party then
			row
					:css('font-style', italic_style)
				:tag('td')
					:wikitext('Total')
		end
		if args['atotal' .. v] and has_cand and has_party and args.dsv or args['atotalnv' .. v] and has_party and args.dsv then
			row
					:css('font-style', italic_style)
				:tag('td')
					:attr('colspan', 3)
					:wikitext('Total')
		end
		if args['votes' .. v] and args['divisor'] 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], (args['row' .. v] == 'Total') and args['votes' .. v] or args['divisor']))
		elseif 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['avotes' .. v] then
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(fmt(args['avotes' .. v]))
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(pct(args['avotes' .. v], valid[1]))
		elseif args['atotal' .. v] and args['divisor'] then
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(fmt(args['atotal' .. v]))
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(pct(args['atotal' .. v], (args['row' .. v] == 'Total') and args['atotal' .. v] or args['divisor']))
		elseif args['ivotes' .. v] or args ['ipct' .. v] or args['atotal' .. v] then
			if ((args['ivotes' .. v] or args['atotal' .. v] or '') ~= '_NO_CELL_') then
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(fmt(args['ivotes' .. v]) or fmt(args['atotal' .. v]))
			end
			if args['ipct' .. v] or args['apct' .. v] or args['atotal' .. v] then
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(args['ipct' .. v] or args['apct' .. v] or pct(args['atotal' .. v], valid[1]))
			else
				row:tag('td')
					:attr('rowspan', args['vspan' .. v])
					:wikitext()
			end
		elseif has_votes and headings['seats'] and headings['sw'] and not has_votesrow and not has_seatsrow and not args['sw' .. v] and not has_rspan1 then
				row:tag('td')
					:attr('colspan', 4)
		elseif has_votes and headings['seats'] and not has_votesrow and not has_seatsrow and not has_rspan1 or has_votes and headings['sw'] and not has_votesrow and not args['sw' .. v] and not has_rspan1 or has_seats2 and not has_votesrow and not has_seatsrow and not has_rspan1 then
				row:tag('td')
					:attr('colspan', 3)
		elseif has_votes and not has_votesrow and not has_rspan1 or has_votes and args['row' .. v] and has_seatsrow and not has_votesrow or has_seats2 and not has_votesrow and not has_rspan1 then
				row:tag('td')
					:attr('colspan', 2)
		elseif args['r1blockc' .. v] then
				row:tag('td')
					:attr('rowspan', args['r1blockr' .. v])
					:attr('colspan', args['r1blockc' .. v])
					:wikitext()
		end
		if headings['sw'] and has_votesrow then
			row
				:tag('td')
					:css('text-align', 'right')
					:attr('rowspan', args['vspan' .. v])
					:wikitext(args['sw' .. v])
		end
		if headings['seats'] and not args['dsv'] and not has_seats2 and rounds < 2 or has_seats2 and args['seats' .. v] or has_seats2 and args['votes' .. v] or has_seats2 and args['iseats' .. v] or has_seats2 and args['aseats' .. v] then
		if args['iseats' .. v] or args['aseats' .. v] then
			row
				:tag('td')
					:css('font-style', italic_style)
					: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'] and args['divisor2'] then
				row:tag('td')
					:attr('rowspan', args['v2span' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['votes' .. v .. '_2']))
				row:tag('td')
					:attr('rowspan', args['v2span' .. v])
					:css('text-align', 'right')
					:wikitext(pct(args['votes' .. v .. '_2'], (args['row' .. v] == 'Total') and args['votes' .. v .. '_2'] or args['divisor2']))
		elseif args['votes' .. v .. '_2'] then
				row:tag('td')
					:attr('rowspan', args['v2span' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['votes' .. v .. '_2']))
				row:tag('td')
					:attr('rowspan', args['v2span' .. v])
					: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')
					:attr('rowspan', args['v2span' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(fmt(args['ivotes' .. v .. '_2']) or fmt(args['avotes' .. v .. '_2']))
				row:tag('td')
					:attr('rowspan', args['v2span' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(args['ipct' .. v .. '_2'] or args['apct' .. v .. '_2'] or pct(args['avotes' .. v .. '_2'], valid[2]))
		elseif has_votes2 and has_seats2 and has_sw2 and not has_votesrow2 and not has_seatsrow2 and not args['sw' .. v .. '_2'] and not has_rspan2 then
				row:tag('td')
					:attr('colspan', 4)
		elseif has_votes2 and has_seats2 and not has_votesrow2 and not has_seatsrow2 and not has_rspan2 or has_votes2 and has_sw2 and not has_votesrow2 and not args['sw' .. v .. '_2'] and not has_rspan2 or has_seats2 and not has_votesrow2 and not has_seatsrow2 and not has_rspan2 then
				row:tag('td')
					:attr('colspan', 3)
		elseif has_votes2 and not has_votesrow2 and not has_rspan2 or has_votes2 and args['row' .. v] and has_seatsrow2 and not has_votesrow2 or has_seats2 and not has_votesrow2 and not has_rspan2 then
				row:tag('td')
					:attr('colspan', 2)
		elseif args['r2blockc' .. v] then
				row:tag('td')
					:attr('rowspan', args['r2blockr' .. v])
					:attr('colspan', args['r2blockc' .. v])
					:wikitext()
		end
		if headings['seats'] and args['dsv'] and not has_sspan or headings['seats'] and rounds > 1 and not has_seats2 and not has_sspan or args['seats' .. v] and has_sspan and rounds > 1 and not has_seats2 then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['seats' .. v]) or fmt(args['aseats' .. v]) or '–')
		end
		if has_sw2 and args['votes' .. v .. '_2'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:attr('rowspan', args['v2span' .. v])
					:wikitext(args['sw' .. v .. '_2'])
		end
		if args['seats' .. v .. '_2'] or has_seats2 and args['votes' .. v .. '_2'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:attr('rowspan', args['s2span' .. v])
					:wikitext(fmt(args['seats' .. v .. '_2']) or '–')
		end
		if args['iseats' .. v .. '_2'] or has_seats2 and args['ivotes' .. v .. '_2'] and not args['seats' .. v .. '_2'] or args['aseats' .. v .. '_2'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:attr('rowspan', args['s2span' .. v])
					:wikitext(fmt(args['iseats' .. v .. '_2']) or fmt(args['aseats' .. v .. '_2']))
		end
		if has_seats3 or has_votes3 then
		if args['votes' .. v .. '_3'] then
				row:tag('td')
					:attr('rowspan', args['v3span' .. v])
					:css('text-align', 'right')
					:wikitext(fmt(args['votes' .. v .. '_3']))
				row:tag('td')
					:attr('rowspan', args['v3span' .. v])
					:css('text-align', 'right')
					:wikitext(pct(args['votes' .. v .. '_3'], valid[3]))
		elseif args['ivotes' .. v .. '_3'] or args ['ipct' .. v.. '_3'] or args['avotes' .. v .. '_3'] then
				row:tag('td')
					:attr('rowspan', args['v3span' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(fmt(args['ivotes' .. v .. '_3']) or fmt(args['avotes' .. v .. '_3']))
				row:tag('td')
					:attr('rowspan', args['v3span' .. v])
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(args['ipct' .. v .. '_3'] or args['apct' .. v .. '_3'] or pct(args['avotes' .. v .. '_3'], valid[3]))
		elseif has_seats3 and not has_votesrow3 and not has_seatsrow3 and not has_rspan3 then
				row:tag('td')
					:attr('colspan', 3)
		elseif has_seats3 and not has_votesrow3 and not has_rspan3 or has_seats3 and args['row' .. v] and has_seatsrow3 and not has_votesrow3 or has_votes3 and not has_votesrow3 and not has_rspan3 then
				row:tag('td')
					:attr('colspan', 2)
		elseif args['r3blockc' .. v] then
				row:tag('td')
					:attr('rowspan', args['r3blockr' .. v])
					:attr('colspan', args['r3blockc' .. v])
					:wikitext()
		end
		if has_sw2 and args['votes' .. v .. '_3'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:attr('rowspan', args['v3span' .. v])
					:wikitext(args['sw' .. v .. '_3'])
		end
		if args['seats' .. v .. '_3'] or has_seats3 and args['votes' .. v .. '_3'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['seats' .. v .. '_3']) or '–')
		end
		if args['iseats' .. v .. '_3'] or has_seats3 and args['ivotes' .. v .. '_3'] and not args['seats' .. v .. '_3'] or args['aseats' .. v .. '_3'] then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['iseats' .. v .. '_3']) or fmt(args['aseats' .. v .. '_3']))
		end
		end
		if args['st1t' .. v] and has_sspan or headings['st1t'] and not has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(args['st1t' .. v] or args['ast1t' .. v] or '–')
		end
		if args['st2t' .. v] and has_sspan or headings['st2t'] and not has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(args['st2t' .. v] or args['ast2t' .. v] or '–')
		end
		if args['st3t' .. v] and has_sspan or headings['st3t'] and not has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(args['st3t' .. v] or args['ast3t' .. v] or '–')
		end
		if args['st4t' .. v] and has_sspan or headings['st4t'] and not has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(args['st4t' .. v] or args['ast4t' .. v] or '–')
		end
		if args['st5t' .. v] and has_sspan or headings['st5t'] and not has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['sspan' .. v])
					:css('text-align', 'right')
					:wikitext(args['st5t' .. v] or args['ast5t' .. v] or '–')
		end
		if args['totseats' .. v] and has_sspan or args['atotseats' .. v] and has_sspan then
			row
				:tag('td')
					:attr('rowspan', args['tsspan' .. v])
					:css('text-align', 'right')
					:wikitext(args['totseats' .. v] or args['atotseats' .. v] or '–')
		end
		if headings['totseats'] and not args['itotseats' .. v] and not args['atotseats' .. v] and not has_sspan then
			row
				:tag('td')
					:css('text-align', 'right')
					:wikitext(fmt(args['totseats' .. v]) or '–')
		elseif headings['totseats'] and not has_sspan then
			row
				:tag('td')
					:css('text-align', 'right')
					:css('font-style', italic_style)
					:wikitext(args['itotseats' .. v] or args['atotseats' .. v])
		end
		if headings['sc'] and args.sctitle then
			row
				:tag('td')
					:wikitext(args['sc' .. v])
		elseif headings['sc'] then
			row
				:tag('td')

					:css('text-align', 'right')
					:wikitext(args['sc' .. v] or '–')
		end
	end
	end

	-- separating line
	if args['valid'] or args['valid2'] or args['valid3'] or args['invalid'] or args['invalid2'] or args['invalid3'] or args['totalvotes'] or args['totalvotes2'] or args['totalvotes3'] or args['electorate'] or args['electorate2'] or args['electorate3'] or args['turnout'] or args['turnout2'] or args['turnout3'] or args['source'] then
	row = root
		:tag('tr')
			:addClass('sortbottom')
	row
		:tag('td')
			:css('color', 'var( --color-base, #202122 )')
			:css('background', 'var( --background-color-neutral, #eaecf0 )')
			:attr('colspan', cols)
	end
	-- valid votes
	if args['invalid'] or args['invalid2']  or args['invalid3'] 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('color', 'inherit')
			:css('background', 'inherit')
	if args['invalid'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[1]))
		:tag('td')
				:wikitext(pct(ovalid[1], ovalid[1] + invalid[1] + blank[1]))
	elseif not args['invalid'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['validsw'] then
	row
		:tag('td')
			:wikitext(args['validsw'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 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] + blank[2]))
	elseif rounds > 1 and not args['invalid2'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if has_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['validsw2'] then
	row
		:tag('td')
			:wikitext(args['validsw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and not args['validsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if args['invalid3'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[3]))
		:tag('td')
				:wikitext(pct(ovalid[3], ovalid[3] + invalid[3] + blank[3]))
	elseif has_seats3 and not args['invalid3'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if args['invalidsw'] and not has_sw2 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) + (has_seats3 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) + (has_seats3 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) + (has_seats3 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['invalidonly'] or args['blank'] then
	row
		:tag('th')
			:wikitext('Invalid votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('color', 'inherit')
			:css('background', 'inherit')
	else
	row
		:tag('th')
			:wikitext('Invalid/blank votes')
			:wikitext(args.invalidnote)
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('color', 'inherit')
			: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] + blank[1]))
	elseif not args['invalid'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['invalidsw'] then
	row
		:tag('td')
			:wikitext(args['invalidsw'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 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] + blank[2]))
	elseif rounds > 1 and not args['invalid2'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['invalidsw2'] then
	row
		:tag('td')
			:wikitext(args['invalidsw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and not args['invalidsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if has_seats3 and args['invalid3'] or has_votes3 and args['invalid3'] then
	row
		:tag('td')
			:wikitext(fmt(invalid[3]))
		:tag('td')
				:wikitext(pct(invalid[3], ovalid[3] + invalid[3] + blank[3]))
	elseif has_seats3 and not args['invalid3'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if args['invalidsw'] and not has_sw2 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) + (has_seats3 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) + (has_seats3 and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	end
	-- blank votes
	if args['blank'] or args['blank2'] or args['blank3'] then
	row = root:tag('tr')
			:addClass('sortbottom')
			:css('text-align', 'right')
	row
		:tag('th')
			:wikitext('Blank votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('font-weight', 'normal')
			:css('color', 'inherit')
			:css('background', 'inherit')
	if args['blank'] then
	row
		:tag('td')
			:wikitext(fmt(blank[1]))
		:tag('td')
			:wikitext(pct(blank[1], ovalid[1] + invalid[1] + blank[1]))
	else
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if has_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['blanksw'] then
	row
		:tag('td')
			:wikitext(args['blanksw'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and not args['blanksw'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if rounds > 1 and args['blank2'] then
	row
		:tag('td')
			:wikitext(fmt(blank[2]))
		:tag('td')
				:wikitext(pct(blank[2], ovalid[2] + invalid[2] + blank[2]))
	elseif rounds > 1 and not args['blank2'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if has_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['blanksw2'] then
	row
		:tag('td')
			:wikitext(args['blanksw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and not args['blanksw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if has_seats3 and args['blank3'] or has_votes3 and args['blank3'] then
	row
		:tag('td')
			:wikitext(fmt(blank[3]))
		:tag('td')
				:wikitext(pct(blank[3], ovalid[3] + invalid[3] + blank[3]))
	elseif has_seats3 and args['blank3'] or has_votes3 and args['blank3'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
			:wikitext()
	end
	if args['blanksw'] and not has_sw2 then
		row:tag('td')
			:wikitext(args['blanksw'])
	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) + (has_seats3 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) + (has_seats3 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(args.tvtitle or 'Total votes')
			:attr('scope', 'row')
			:attr('colspan', cs)
			:css('text-align', 'left')
			:css('background', 'inherit')
			:css('color', 'inherit')
	if not args['totalvotes'] and args['invalid'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[1] + invalid[1] + blank[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_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['invalidsw'] then
	row
		:tag('td')
			:wikitext('–')
		:tag('td')
			:wikitext()
	end
	if has_seats2 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] + blank[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_seats2 and not headings['sw'] then
	row
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and args['invalidsw2'] then
	row
		:tag('td')
			:wikitext('–')
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and not args['invalidsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	if has_seats3 and not args['totalvotes3'] and args['invalid3'] or has_votes3 and not args['totalvotes3'] and args['invalid3'] then
	row
		:tag('td')
			:wikitext(fmt(ovalid[3] + invalid[3] + blank[3]))
		:tag('td')
			:wikitext(pct(1, 1))
	elseif has_seats3 and args['totalvotes3'] or has_votes3 and args['totalvotes3'] then
	row
		:tag('td')
			:wikitext(fmt(args.totalvotes3))
		:tag('td')
			:wikitext('–')
	elseif has_seats3 and not args['invalid3'] or has_votes3 and not args['invalid3'] then
	row
		:tag('td')
			:wikitext()
		:tag('td')
				:wikitext()
	end
	if args['invalidsw'] and not has_sw2 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) + (has_seats3 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) + (has_seats3 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('color', 'inherit')
			:css('background', 'inherit')
	row
			:tag('td')
				:wikitext(fmt(electorate[1]))
	if args['invalid'] and args['electorate'] and not args['totalvotes'] then
		row
			:tag('td')
				:wikitext(args.turnout or pct(ovalid[1] + invalid[1] + blank[1], electorate[1]))
	elseif args['totalvotes'] and args['electorate'] then
		row
			:tag('td')
				:wikitext(args.turnout or 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_seats2 and not headings['sw'] then
		row
			:tag('td')
				:wikitext()
	end
	if has_seats2 and headings['sw'] and args['turnoutsw'] then
	row
		:tag('td')
			:wikitext(args['turnoutsw'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 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(args.turnout2 or pct(ovalid[2] + invalid[2] + blank[2], electorate[2]))
	elseif args['totalvotes2'] and args['electorate2'] then
		row
			:tag('td')
				:wikitext(args.turnout2 or 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_seats2 and not headings['sw'] then
		row
			:tag('td')
				:wikitext()
	end
	if has_seats2 and headings['sw'] and args['turnoutsw2'] then
	row
		:tag('td')
			:wikitext(args['turnoutsw2'])
		:tag('td')
			:wikitext()
	end
	if has_seats2 and headings['sw'] and not args['turnoutsw2'] then
	row
		:tag('td')
			:attr('colspan', 2)
			:wikitext()
	end
	end
	if has_seats3 or has_votes3 then
		row
			:tag('td')
				:wikitext(fmt(electorate[3]))
	if args['invalid3'] and args['electorate3'] then
		row
			:tag('td')
				:wikitext(args.turnout3 or pct(ovalid[3] + invalid[3] + blank[3], electorate[3]))
	elseif args['totalvotes3'] and args['electorate3'] then
		row
			:tag('td')
				:wikitext(args.turnout3 or pct(totalvotes[3], electorate[3]))
	elseif args['electorate3'] then
		row
			:tag('td')
				:wikitext(args.turnout3 or '–')
	else
		row
			:tag('td')
				:wikitext(args.turnout3)
	end
	end
	if args['turnoutsw'] and not has_sw2 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) + (has_seats3 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) + (has_seats3 and 1 or 0)
		row
			:tag('td')
				:attr('colspan', cspan > 1 and cspan or nil)
	end
	end
	if args.turnoutneeded then
		row = root:tag('tr')
				:addClass('sortbottom')
				:css('background-color', '#f2f2f2')
		row
			:tag('td')
				:attr('colspan', '3')
				:wikitext('Turnout needed')
			:tag('td')
				:css('text-align', 'right')
				:wikitext(args['turnoutneeded'])
	end
	if args['majority'] then
	if args['invalid'] or args['electorate'] then
	row = root
		:tag('tr')
			:addClass('sortbottom')
	row
		:tag('td')
			:css('color', 'var( --color-base, #202122 )')
			:css('background', 'var( --background-color-neutral, #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('color', 'inherit')
			: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 = get_color(args['resultcolour'] or nil, args['result'])
		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('color', 'inherit')
			: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 = get_color(args['resultcolour'] or nil, args['result2'])
		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