Jump to content

Module:CFB schedule/sandbox

From Wikipedia, the free encyclopedia
local p = {}
local horizontal = require('Module:List').horizontal
local yesno = require("Module:Yesno")
local dagger = '<sup class="CFB-schedule-hcgame">[[File:Dagger-14-plain.png|alt=dagger|link=]]</sup>'

local haslocgamename = false
local hasoppgamename = false
local haslocrivalry = false
local hasopprivalry = false
local hasrank = false
local hasstrangescore = false
local hasnowrap = false

local function isnotempty(s)
	return s and s:match( '^%s*(.-)%s*$' ) ~= ''
end

local function ifexist(page)
	if not page then return false end
	if mw.title.new(page).exists then return true end
	return false
end

local function getdivision(y)
	if y >= 2006 then
		return 'NCAA Division I FBS'
	elseif y >= 1978 then
		return 'NCAA Division I-A'
	elseif y >= 1973 then
		return 'NCAA Division I'
	elseif y >= 1956 then
		return 'NCAA University Division'
	elseif y >= 1910 then
		return 'NCAA'
	elseif y >= 1906 then
		return 'Intercollegiate Athletic Association of the United States'
	else
		return 'college'
	end
end

local function getpolltext(y, d, p)
	-- default poll is the Coaches poll
	if (p or '') == '' then
		p = 'Coaches\''
	end

	-- if p is linked then just return p
	if (p or ''):find('[%[%]]') then
		return p
	end

	-- else if y is a number
	if isnotempty(y) and tonumber(y) then
		if (d or '') == '' then
			d = getdivision(tonumber(y))
		end

		return '[[' .. y .. ' ' .. d .. ' football rankings'
					.. '#' .. p .. ' Poll|' .. p .. ' Poll]]'
					.. ' released prior to the game'
	end

	-- else if d is not empty
	if isnotempty(d) then
		d = d .. ' '

		if ifexist(d .. p .. ' Poll') then
			return '[[' .. d .. p .. ' Poll|' .. p .. ' Poll]]'
				.. ' released prior to the game'
		end
	end

	-- else if p Poll is an article
	if ifexist(p .. ' Poll') then
		return (d or '') .. '[[' .. p .. ' Poll]]'
			.. ' released prior to the game'
	end

	return (d or '') .. p .. ' poll released prior to the game'
end

local function getopp(s, atvs, movegn)
	s = mw.ustring.gsub(s, '<[%s/]*[Nn][Cc][Gg][%s/]*>', '<span class="CFB-schedule-ncgame">*</span>')
	s = mw.ustring.gsub(s, '<[%s/]*[Hh][Cc][%s/]*>', dagger)
	atvs = mw.ustring.gsub(atvs or '', '^%s*@%s*', 'at')
	atvs = mw.ustring.gsub(atvs or '', '^%s*[Vv][Ss]?[%.%s]*', 'vs.')
	s = mw.ustring.gsub(s, '^([A-Za-z%.%s]*)[Nn][Oo][%.%s]*([0-9])', '%1 No.&nbsp;%2' )
	s = mw.ustring.gsub(s, '^([A-Za-z%.%s]*)#([0-9])', '%1 No.&nbsp;%2')
	if mw.ustring.match(s, 'No%.&nbsp;%d') then hasrank = true end
	local gn, r = '', ''
	if mw.ustring.match(s, '[Nn][Oo][Ww][Rr][Aa][Pp]') 
		or mw.ustring.match(s, '[Nn][Bb][Ss][Pp]')
	then
		hasnowrap = true
	end
	if mw.ustring.match(s, '^.*%s*%(%s*%[%[[^%[%]]*%]%]%s*%)%s*.*$') then
		if mw.ustring.match(s, '%(%[%[[^%[%]]*%|%s*[Rr]ivalry') then
			hasopprivalry = true
		else
			hasoppgamename = true
		end
		if movegn == true then
			s, gn, r = mw.ustring.match(s, '^(.*)%s*%(%s*(%[%[[^%[%]]*%]%])%s*%)(%s*.*)$')
		end
	end
	if atvs ~= '' then atvs = atvs .. ' ' end
	return atvs .. s .. r, gn
end

local function getrank(s)
	s = mw.ustring.gsub(s, '^%s*(%d+)', 'No.&nbsp;%1')
	s = mw.ustring.gsub(s, '^%s*(T)[%-–%s]*(%d+)', 'No.&nbsp;%1–%2')
	return s
end

local function getsite(stadium, location, game_name)
	if isnotempty(stadium) and isnotempty(location) then
		game_name = mw.ustring.gsub(game_name, '^%s*', '')
		if isnotempty(game_name) then
			game_name = mw.ustring.gsub(game_name, '^([Rr][Ii][Vv][Aa][Ll][Rr][Yy])$', '%1[[Category:Pages using CFB schedule with an unlinked rivalry]]')
			game_name = mw.ustring.gsub(game_name, '^([^%[%]]*)$', '%1[[Category:Pages using CFB schedule with an unlinked gamename]]')
			game_name = ' (' .. game_name .. ')'
		end
		
		if mw.ustring.match(location, '%s%(%[%[') then
			if mw.ustring.match(location, '%s%(%[%[[^%]]*%|%s*[Rr]ivalry') then
				haslocrivalry = true
			else
				haslocgamename = true
			end
		end
		return horizontal({stadium, location .. game_name})
	else
		game_name = mw.ustring.gsub(game_name, '^%s*', '')
		if isnotempty(game_name) then
			game_name = ' (' .. game_name .. ')'
		end
		return stadium .. location .. game_name
	end
end

local function setbg(WL,N)
	local BG = 'inherit'
	WL = mw.ustring.match(WL, '^%s*(.-)%s*$')
	WL = WL:upper()
	if WL == 'W' then
		BG = '#DDFFDD'
	elseif WL == 'L' then
		BG = '#FFDDDD'
	elseif WL == 'T' then
		BG = '#FFFFE6'
	elseif WL == 'V' then
		BG = '#F0E8E8'
		WL = 'W'
		N = N .. ' (vacated)'
	end
	return BG, WL, N
end

local function parse4(p, s)
	local t = {}
	for k = 1,4 do
		t[k] = mw.ustring.gsub(s, p, '%' .. k)
	end
	local wl, sc, n, bg = t[1], t[2] .. '–' .. t[3], t[4], 'inherit'
	bg, wl, n = setbg(wl, n)
	return '<span style="display:inline-block; font-weight:bold; width:1em">' .. wl .. '</span> ' .. sc .. n, bg
end
	
local function getresult(wl, s, n)
	local bg = 'inherit'

	local loopnum = 0
	while (mw.ustring.match(s, '&[Nn][Bb][Ss][Pp];') and loopnum < 5) do
		hasstrangescore = true
		s = mw.ustring.gsub(s, '&[Nn][Bb][Ss][Pp];', ' ')
		s = mw.ustring.gsub(s, '<%s*[Ss][Pp][Aa][Nn][^<>]*>%s*</[Ss][Pp][Aa][Nn]%s*>', ' ')
		loopnum = loopnum + 1
	end
	s = mw.ustring.gsub(s, '&[Nn][Bb][Ss][Pp];%s*(<[%s/]*[0-9]*OT[%s/]*>)', ' %1')
	s = mw.ustring.gsub(s, '%s*<([0-9]*)[Oo][Tt]>', ' <sup>%1OT</sup>')
	s = mw.ustring.gsub(s, '&[MmNn][Dd][Aa][Ss][Hh];', '–')
	s = mw.ustring.gsub(s, '<span class="url">(.-)</span>', '%1')
	s = mw.ustring.gsub(s, '^%s*(.-)%s*$', '%1')

	if wl ~= '' then
		s = mw.ustring.gsub(s, '^%s*(%d+)%s*[%‐‒–—―]%s*', '%1–')
		s = mw.ustring.gsub(s, '^%s*(%[%[%s*[^|]*|%s*%d+)%s*[%‐‒–—―]%s*', '%1–')
		s = mw.ustring.gsub(s, '^%s*(%[[^|%[%]%s]*%s+%d+)%s*[%‐‒–—―]%s*', '%1–')
		local r
		if mw.ustring.match(wl, '^%s*[%a]?%s*$') then
			bg, wl, n = setbg(wl, n)
			r = '<span style="display:inline-block; font-weight:bold; width:1em">' 
				.. wl .. '</span> ' .. s .. n
		else
			hasstrangescore = true
			r = wl .. s .. n
		end
		return r,bg
	end
	
	if s == 'Cancelled' or s == '' or s == '?' then
		return wl .. s .. n, 'inherit'
	end
	
	if mw.ustring.match(s, '^[%a]%s+[^%d]') then
		wl = mw.ustring.gsub(s, '^(%a)%s+(.-)$', '%1')
		s = mw.ustring.gsub(s, '^(%a)%s+(.-)$', '%2')
		bg, wl, n = setbg(wl, n)
		local r = '<span style="display:inline-block; font-weight:bold; width:1em">'
			.. wl .. '</span> ' .. s .. n
		return r, bg
	end
	
	if mw.ustring.match(s, '^[%a]$') then
		bg, wl, n = setbg(s, n)
		local r = '<span style="display:inline-block; font-weight:bold; width:1em">'
			.. wl .. '</span> ' .. n
		return r, bg
	end

	local pat
	pat = '^(%a)%s*(%d+)[%D]%s*(%d+)(.-)$'
	if mw.ustring.match(s, pat) then
		return parse4(pat, s)
	end

	pat = '^(%a)%s*(%[%[%s*[^|]*|%s*%d+)[%D]%s*(%d+%]%])(.-)$'
	if mw.ustring.match(s, pat) then
		return parse4(pat, s)
	end
	
	pat = '^([%a])%s*(%[[^|%[%]%s]*%s+%d+)[%D]%s*(%d+%s*%])(.-)$'
	if mw.ustring.match(s, pat) then
		return parse4(pat, s)
	end
	
	hasstrangescore = true

	return wl .. s .. n, 'inherit'
end

local function getfootnotes(ncg, hc, oe, rank, opprank, poll, tz, src)
	-- footnotes
	local fn = {}
	if ncg then
		table.insert(fn,'*Non-conference game')
	end
	if hc then
		table.insert(fn, dagger .. 'Homecoming')
	end
	if isnotempty(oe) then
		table.insert(fn, oe)
	end
	if (rank == true) or (opprank == true) then
		table.insert(fn, 'Rankings from ' .. poll)
	end
	if isnotempty(tz) then
		table.insert(fn,'All times are in [[' .. tz .. ' Time Zone|' .. tz .. ' time]]')
	end
	if isnotempty(src) then
		table.insert(fn, 'Source: ' .. src)
	end

	if (#fn > 0) then
		return horizontal(fn)
	else
		return nil
	end
end

local function make_outer_table(args)
	if not args[1] then
		return '[[Category:Pages using CFB schedule with no content]]'
	end
	local showdate   = yesno(args['date'], false)
	local showtime   = yesno(args['time'], false)
	local showrank   = yesno(args['rank'], false)
	local showtv     = yesno(args['tv'], false)
	local showattend = yesno(args['attend'], false)
	local showsource = yesno(args['source'], false)
	local ncg, hc = false, false
	local row

	-- Step 1: Inspect the rows to determine which headers are active
	for _, v in ipairs(args) do
		if showdate == false then
			if v:find('<td[^>]*CFB%-schedule%-date[^>]*>%s*[^%s<]') then
				showdate = true
			end
		end
		if showtime == false then
			if v:find('<td[^>]*CFB%-schedule%-time[^>]*>%s*[^%s<]') then
				showtime = true
			end
		end
		if showrank == false then
			if v:find('<td[^>]*CFB%-schedule%-rank[^>]*>%s*[^%s<]') then
				showrank = true
			end
		end
		if showtv == false then
			if v:find('<td[^>]*CFB%-schedule%-tv[^>]*>%s*[^%s<]') then
				showtv = true
			end
		end
		if showattend == false then
			if v:find('<td[^>]*CFB%-schedule%-attend[^>]*>%s*[^%s<]') then
				showattend = true
			end
		end
		if showsource == false then
			if v:find('<td[^>]*CFB%-schedule%-source[^>]*>%s*[^%s<]') then
				showsource = true
			end
		end
		if ncg == false then
			if v:find('<s[pu][ap][^>]*CFB%-schedule%-ncgame') then
				ncg = true
			end
		end
		if hc == false then
			if v:find('<s[pu][ap][^>]*CFB%-schedule%-hcgame') then
				hc = true
			end
		end
		if hasrank == false then
			if showrank == true or v:find('No%.&nbsp;%d') or v:find('No%.&nbsp;T[%-–%s]*%d') then
				hasrank = true
			end
		end
	end
	
	-- Step 2: Build the table
	local root = mw.html.create('table')
	root:addClass('wikitable')
		:css('font-size', '95%')

	-- optional caption
	if args['caption'] then
		root:tag('caption'):wikitext(args['caption'])
	end
	
	-- add the headers
	local cols = 3
	row = root:tag('tr')
	if showdate then
		row:tag('th'):wikitext('Date')
		cols = cols + 1
	end
	if showtime then
		row:tag('th'):wikitext('Time')
		cols = cols + 1
	end
	row:tag('th'):wikitext('Opponent')
	if showrank then
		row:tag('th'):wikitext('Rank')
		cols = cols + 1
	end
	row:tag('th'):wikitext('Site')
	if showtv then
		row:tag('th'):wikitext('TV')
		cols = cols + 1
	end
	row:tag('th'):wikitext('Result')
	if showattend then
		row:tag('th'):wikitext('Attendance')
		cols = cols + 1
	end
	if showsource then
		row:tag('th'):wikitext('Source')
		cols = cols + 1
	end

	local k = 1
	while args[k] ~= nil do
		row = args[k]
		if showdate then
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-date[^>]*>', '<td style="white-space:nowrap">')
		else
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-date[^>]*>%s*</td>%s*', '')
		end
		if showtime then
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-time[^>]*>', '<td style="white-space:nowrap">')
		else
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-time[^>]*>%s*</td>%s*', '')
		end
		if showrank then
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-rank[^>]*>', '<td style="white-space:nowrap">')
		else
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-rank[^>]*>%s*</td>%s*', '')
		end
		if showtv then
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-tv[^>]*>', '<td>')
		else
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-tv[^>]*>%s*</td>%s*', '')
		end
		if showattend then
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-attend[^>]*>', '<td style="text-align:center">')
		else
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-attend[^>]*>%s*</td>%s*', '')
		end
		if showsource then
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-source[^>]*>', '<td style="text-align:center">')
		else
			row = mw.ustring.gsub(row, '<td[^>]*CFB%-schedule%-source[^>]*>%s*</td>%s*', '')
		end
		root:wikitext(row)
		k = k + 1
	end

	-- footnotes
	local fnotes = getfootnotes(
		ncg,
		hc,
		args['other-event'] or args['other_event'] or args['otherevent'],
		showrank,
		yesno(args['opprank'], hasrank),
		getpolltext(
			args['rank_year'] or args['rankyear'],
			args['rank_division'] or args['rankdivision'],
			args['poll']
		),
		showtime and args['timezone'] or '',
		args['seasonsource']
	)

	if fnotes ~= nil then
		root:tag('tr')
				:tag('td')
				:attr('colspan',cols)
				:css('font-size', '85%')
				:wikitext(fnotes)
	end
	return tostring(root)
end

local function convert_table(args)
	local function splitresult(s)
		s = mw.ustring.gsub(s or '', '&[MmNn][Dd][Aa][Ss][Hh];', '–')
		s = mw.ustring.match(s, '^%s*(.-)%s*$')
		if mw.ustring.match(s, '^[%a]%s*%d+[%D]%s*%d+%s*.*$') then
			local t = {}
			for k = 1,4 do
				t[k] = mw.ustring.gsub(s,'^([%a])%s*(%d+)[%D]%s*(%d+)%s*(.*)$', '%' .. k)
			end
			local wl, s1, s2, n = t[1], t[2], t[3], t[4]
			wl = wl:lower()
			return wl, s1 .. '–' .. s2 .. n
		end
		return '', s
	end
	local res = '{{CFB schedule\n'
	res = res .. (yesno(args['opprank'], false) == false and '' or '| opprank = y\n')
			  .. (isnotempty(args['other-event'])
			and '| other-event = ' .. args['other-event'] .. '\n' or '')
			  .. (isnotempty(args['rankyear'])
			and '| rankyear = ' .. args['rankyear'] .. '\n' or '')
			  .. (isnotempty(args['rankdivision'])
			and '| rankdivision = ' .. args['rankdivision'] .. '\n' or '')
			  .. (isnotempty(args['poll'])
		and '| poll = ' .. args['poll'] .. '\n' or '')
			  .. (isnotempty(args['timezone'])
		and '| timezone = ' .. args['timezone'] .. '\n' or '')

	-- switch headers on and off
	local headers = {'Date', 'Time', 'At/Vs', 'Opponent', 'Rank', 'Site', 'Location', 'TV', 'Result', 'Attendance', 'Source'}
	local resultoffset = 8

	for k = #headers,1,-1 do
		if headers[k] == 'Time' and (yesno(args['time'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'At/Vs' and (yesno(args['atvs'], true) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'Rank' and (yesno(args['rank'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'TV' and (yesno(args['tv'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'Attendance' and (yesno(args['attend'], false) == false) then
			table.remove(headers,k)
		elseif headers[k] == 'Source' and (yesno(args['source'], false) == false) then
			table.remove(headers,k)
		end
	end

	-- parse the table
	local k = 1
	while args[k] ~= nil do
		res = res .. '|{{CFB schedule entry\n'
		for j = 1,#headers do
			if headers[j] == 'Date' then
				res = res .. '| date = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			elseif headers[j] == 'Time' then
				res = res .. '| time = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			elseif headers[j] == 'At/Vs' then
				local atvs = mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1')
				atvs = mw.ustring.gsub(atvs, '^@', 'at')
				atvs = mw.ustring.gsub(atvs, '^[Vv][Ss]?[%.%s]*', 'vs.')
				if mw.ustring.find(atvs, '^at') then
					res = res .. '| away = y\n'
				elseif mw.ustring.find(atvs, '^vs') then
					res = res .. '| neutral = y\n'
				elseif atvs ~= '' then
					res = res .. '| atvs = ~' .. atvs .. '~\n'
				end
			elseif headers[j] == 'Opponent' then
				local opp = mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1')
				if mw.ustring.find(opp, '%s*<[%s/]*[Nn][Cc][Gg][%s/]*>%s*') then
					opp = mw.ustring.gsub(opp, '%s*<[%s/]*[Nn][Cc][Gg][%s/]*>%s*', ' ')
					res = res .. '| nonconf = y\n'
				end
				if mw.ustring.find(opp, '%s*<[%s/]*[Hh][Cc][%s/]*>%s*') then
					opp = mw.ustring.gsub(opp, '%s*<[%s/]*[Hh][Cc][%s/]*>%s*', ' ')
					res = res .. '| homecoming = y\n'
				end
				opp = mw.ustring.gsub(opp, '^%s*(.-)%s*$', '%1')
				opp = mw.ustring.gsub(opp, '^[Nn][Oo][%.%s]*([0-9])', '#%1' )
				if mw.ustring.find(opp, '^#([0-9]+)%s*') then
					local orank = mw.ustring.gsub(opp, '^#([0-9]+)%s*(.-)$', '%1' )
					opp = mw.ustring.gsub(opp, '^#([0-9]+)%s*(.-)$', '%2' )
					res = res .. '| opprank = ' .. orank .. '\n'
				end
				if mw.ustring.find(opp, '^(.-)%s*%((%[%[[^%[%]]*%]%])%)%s*$') then
					local rgame = mw.ustring.gsub(opp, '^(.-)%s*%((%[%[[^%[%]]*%]%])%)%s*$', '%2')
					opp = mw.ustring.gsub(opp, '^(.-)%s*%((%[%[[^%[%]]*%]%])%)%s*$', '%1')
					res = res .. '| gamename = ' .. rgame .. '\n'
				end
				res = res .. '| opponent = ' .. opp .. '\n'
			elseif headers[j] == 'Rank' then
				local mrank = mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1')
				mrank = mw.ustring.gsub(mrank, '^[Nn][Oo][%.%s]*([0-9])', '%1' )
				mrank = mw.ustring.gsub(mrank, '^#[%.%s]*([0-9])', '%1' )
				res = res .. '| rank = ' .. mrank .. '\n'
			elseif headers[j] == 'Site' then
				res = res .. '| stadium = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			elseif headers[j] == 'Location' then
				local loc = mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1')
				local rname = ''
				if mw.ustring.find(loc, '^(.-)%s*%((%[%[[^%[%]]*%]%])%)%s*$') then
					rgame = mw.ustring.gsub(loc, '^(.-)%s*%((%[%[[^%[%]]*%]%])%)%s*$', '%2')
					rname = rname .. '| gamename = ' .. rgame .. '\n'
					loc = mw.ustring.gsub(loc, '^(.-)%s*%((%[%[[^%[%]]*%]%])%)%s*$', '%1')
				end
				res = res .. '| cityst = ' .. loc .. '\n'
						  .. rname
			elseif headers[j] == 'TV' then
				res = res .. '| tv = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			elseif headers[j] == 'Result' then
				local wl, score = splitresult(args[k] or '')
				res = res .. '| w/l = ' .. wl .. '\n'
						  .. '| score = ' .. score .. '\n'
			elseif headers[j] == 'Attendance' then
				res = res .. '| attend = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			elseif headers[j] == 'Source' then
				res = res .. '| source = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			else
				res = res .. '| ?? = ' .. mw.ustring.gsub(args[k] or '', '^%s*(.-)%s*$', '%1') .. '\n'
			end
			k = k + 1
		end
		res = res .. '}}\n'
	end
	res = res .. '}}'

	return res
end

local function make_table(args)
	if args[1] == nil then return '[[Category:Pages using CFB schedule with no content]]' end
	local hasgamename = true

	-- switch headers on and off
	local headers = {'Date', 'Time', 'At/Vs', 'Opponent', 'Rank', 'Site', 'Location', 'Game name', 'TV', 'Result', 'Attendance', 'Source'}
	local resultoffset = 9

	for k = #headers,1,-1 do
		if headers[k] == 'Time' and (yesno(args['time'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'At/Vs' and (yesno(args['atvs'], true) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'Rank' and (yesno(args['rank'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'Game name' and (yesno(args['gamename'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
			hasgamename = false
		elseif headers[k] == 'TV' and (yesno(args['tv'], false) == false) then
			table.remove(headers,k)
			resultoffset = resultoffset - 1
		elseif headers[k] == 'Attendance' and (yesno(args['attend'], false) == false) then
			table.remove(headers,k)
		elseif headers[k] == 'Source' and (yesno(args['source'], false) == false) then
			table.remove(headers,k)
		end
	end

	-- create the root table
	local root = mw.html.create('table')
	root:addClass('wikitable')
		:css('font-size', '95%')
	
	-- optional caption
	if args['caption'] then
		root:tag('caption'):wikitext(args['caption'])
	end

	-- add the headers
	local row = root:tag('tr')
	for k=1,#headers do
		if headers[k] == 'Rank' then
			local cell = row:tag('th')
			cell:wikitext('Rank')
		elseif headers[k] == 'Location' then
		elseif headers[k] == 'At/Vs' then
		elseif headers[k] == 'Opponent' then
			local cell = row:tag('th')
			cell:wikitext('Opponent')
		else
			local cell = row:tag('th')
			cell:wikitext(headers[k])
		end
	end

	-- build the table
	local ncg, hc = false, false
	local k = 1
	while args[k] ~= nil do
		local res, bg = getresult('', args[k+resultoffset] or '', '')
		row = root:tag('tr'):css('background-color', bg)
		local op, gn = '', ''
		for j = 1,#headers do
			if headers[j] == 'Result' then
				row:tag('td'):css('white-space', 'nowrap'):wikitext(res)
			elseif headers[j] == 'At/Vs' then
			elseif headers[j] == 'Opponent' then
				if mw.ustring.find(args[k] or '', '<[%s/]*[Nn][Cc][Gg][%s/]*>') then
					ncg = true
				end
				if mw.ustring.find(args[k] or '', '<[%s/]*[Hh][Cc][%s/]*>') then
					hc = true
				end
				op, gn = getopp(args[k], (yesno(args['atvs'], true) == true) and (args[k-1] or '') or '', true)
				row:tag('td'):wikitext(op)
			elseif headers[j] == 'Rank' then
				row:tag('td'):wikitext(getrank(args[k]))
			elseif headers[j] == 'Site' then
				row:tag('td'):wikitext(getsite(args[k] or '', args[k+1] or '',
					(hasgamename and (args[k+2] or '') or '') .. (' ' .. gn)))
			elseif headers[j] == 'Location' then
			elseif headers[j] == 'Game name' then
			elseif headers[j] == 'Attendance' then
				row:tag('td'):css('text-align', 'center'):wikitext(args[k])
			else
				row:tag('td'):wikitext(args[k])
			end
			k = k + 1
		end
	end

	-- footnotes
	local fnotes = getfootnotes(
		ncg,
		hc,
		args['other-event'] or args['other_event'] or args['otherevent'],
		yesno(args['rank'], false),
		yesno(args['opprank'], hasrank),
		getpolltext(
			args['rank_year'] or args['rankyear'],
			args['rank_division'] or args['rankdivision'],
			args['poll']
		),
		(yesno(args['time'], false) == false) and '' or args['timezone'],
		args['seasonsource']
	)

	if fnotes ~= nil then
		row = root:tag('tr')
		row:tag('td')
			:attr('colspan',#headers)
			:css('font-size', '85%')
			:wikitext(fnotes)
	end

	-- return the root table
	return tostring(root) ..
		(haslocgamename and '[[Category:Pages using CFB schedule with gamename after location]]' or '') ..
		(hasoppgamename and '[[Category:Pages using CFB schedule with gamename after opponent]]' or '') ..
		(haslocrivalry and '[[Category:Pages using CFB schedule with rivalry after location]]' or '') ..
		(hasopprivalry and '[[Category:Pages using CFB schedule with rivalry after opponent]]' or '') ..
		(hasstrangescore and '[[Category:Pages using CFB schedule with an unusual score]]' or '') ..
		(hasnowrap and '[[Category:Pages using CFB schedule with nowrap or nbsp opponent]]' or '')
end

function p.entry(frame)
	local args = (frame.args.opponent ~= nil) and frame.args or frame:getParent().args
	local cell

	if args['overtime'] then
		args['overtime'] = ' <sup>' .. args['overtime'] .. '</sup>'
	end

	local res, bg = getresult(
		(args['w/l'] or '') .. ' ', 
		args['score'] or '', 
		args['overtime'] or ''
		)

	local root = mw.html.create('tr')
		:addClass('CFB-schedule-row')
		:css('background-color', bg)

	-- Date
	cell = root:tag('td'):addClass('CFB-schedule-date')
	if yesno(args.date, true) then -- won't trigger if args.date is nil, since yesno always yields nil if its input is nil
		cell
			:css('white-space','nowrap')
			:wikitext(args.date)
	else
		cell:css('display','none')
	end

	-- Time
	cell = root:tag('td'):addClass('CFB-schedule-time')
	if yesno(args.time, true) then
		cell
			:css('white-space','nowrap')
			:wikitext(args.time)
	else
		cell:css('display','none')
	end

	-- Opponent
	local op, gn = getopp(
		(isnotempty(args.opprank) and getrank(args.opprank) .. ' ' or '') ..
		(args.opponent or '') ..
		((yesno(args.nonconf,false) == true) and '<ncg>' or '') ..
		((yesno(args.homecoming,false) == true) and '<hc>' or '') ..
		(args.ref or ''),
		(isnotempty(args.away) and 'at' or '') ..
		(isnotempty(args.neutral) and 'vs.' or ''),
		false
		)
	root:tag('td')
		:css('white-space', 'nowrap')
		:wikitext(op)

	-- Rank
	cell = root:tag('td'):addClass('CFB-schedule-rank')
	if yesno(args.rank, true) then -- won't trigger if args.rank is nil, since yesno always yields nil if its input is nil
		if rank ~= '' then
			rank = 'No. ' .. rank
		end
		cell
			:css('text-align','center')
			:css('white-space','nowrap')
			:wikitext(rank)
	else
		cell:css('display','none')
	end

	-- Gamename
	local gamename = args.gamename or ''

	-- Site
	root:tag('td')
		:wikitext(getsite(args.stadium or args.site_stadium or '', (args.cityst or args.site_cityst or ''), gamename))

	-- TV
	cell = root:tag('td'):addClass('CFB-schedule-tv')
	if yesno(args.tv, true) then -- won't trigger if args.tv is nil
		cell
			:wikitext(args.tv)
	else
		cell:css('display','none')
	end

	-- Result
	root:tag('td')
		:css('white-space','nowrap')
		:wikitext(res)

	-- Attendance
	cell = root:tag('td'):addClass('CFB-schedule-attend')
	if yesno(args.attend, true) then -- won't trigger if args.attend is nil
		cell
			:css('text-align','center')
			:wikitext(args.attend)
	else
		cell:css('display','none')
	end

	-- Source
	cell = root:tag('td'):addClass('CFB-schedule-source')
	if yesno(args.source, true) then -- won't trigger if args.source is nil
		cell
			:css('text-align','center')
			:wikitext(args.source)
	else
		cell:css('display','none')
	end

	return tostring(root)
end

function p.subst(frame)
	local args = frame.args[1] and frame.args or frame:getParent().args
	if (args[1] or ''):find('<tr[^>]*CFB%-schedule%-row') then
		return make_outer_table(args)
	else
		return convert_table(args)
	end
end

function p.table(frame)
	local args = frame.args[1] and frame.args or frame:getParent().args
	if (args[1] or ''):find('<tr[^>]*CFB%-schedule%-row') then
		return make_outer_table(args) .. '[[Category:Pages using CFB schedule with named parameters]]'
	else
		return make_table(args) .. '[[Category:Pages using CFB schedule with unnamed parameters]]'
	end
end

return p