Module:Scoreboard

From Leaguepedia | League of Legends Esports Wiki
Jump to: navigation, search

To edit the documentation or categories for this module, click here.

This module can be called via a number of templates, available at Category:Scoreboard Templates. They should (almost) never be filled in by hand, instead we have parsers that create the input.


local util_args = require('Module:ArgsUtil')
local util_cargo = require('Module:CargoUtil')
local util_esports = require('Module:EsportsUtil')
local util_footnote = require('Module:FootnoteUtil')
local util_html = require('Module:HTMLUtil')
local util_math = require('Module:MathUtil')
local util_smw = require('Module:SMWUtil')
local util_table = require('Module:TableUtil')
local util_text = require('Module:TextUtil')
local util_time = require('Module:TimeUtil')
local util_toggle = require('Module:ToggleUtil')
local util_vars = require('Module:VarsUtil')
local m_champion = require('Module:Champion')
local m_role = require('Module:Role')
local m_team = require('Module:Team')
local i18n = require('Module:i18nUtil')
local Sprite = require('Module:Sprite').sprite
local SEASON
local PLAYER_NUMBER = 5
local PLAYER_SIDES = { 'blue', 'red' } -- this is whatever the args in the template want
local TEAMS = { 'blue', 'red' } -- this is absolute for CSS classes
local PLAYER_COS = { 'blue', 'purple' } -- this is for smw
local PRINT_ITEMS = true
local PLAYERS = {}
local PLAYER_ARGS = { 'Champion', 'Link', 'Name', 'SummonerSpells', 'Items', 'Trinket', 'Kills', 'Deaths', 'Assists', 'Gold', 'CS', 'Keystone', 'Secondary', 'nocargo' }
local GAME_ARGS_TEAMS = {
	score = 'Score',
	d = 'Dragons',
	b = 'Barons',
	t = 'Towers',
	rh = 'RiftHeralds',
	i = 'Inhibitors',
	g = 'Gold',
	k = 'Kills',
}
local GAME_ARGS = {
	gamename = 'Gamename',
	patch = 'Patch',
	gamelength = 'Gamelength',
	dst = 'DST',
	statslink = 'MatchHistory',
	vodlink = 'VOD',
	lolvod = 'VOD2'
}
local TOGGLES = {
	one = {
		show_attr = '.sb-w%s-g%s',
		hide_attr = '.sb-showbutton-w%s-g%s',
		show_class = 'sb-allw sb-w%s-allg sb-w%s-g%s',
		hide_class = 'sb-showbutton-allw sb-showbutton-w%s-allg sb-showbutton-w%s-g%s',
	},
	tab = {
		show_attr = '.sb-w%s-allg',
		hide_attr = '.sb-showbutton-w%s-allg',
		show_class = 'sb-allw sb-w%s-allg',
		hide_class = 'sb-showbutton-allw sb-showbutton-w%s-allg',
		cssclass = 'expand-contract-button-no-margin',
	},
	all = {
		show_attr = '.sb-allw',
		hide_attr = '.sb-showbutton-allw',
		show_class = 'sb-allw',
		hide_class = 'sb-showbutton-allw',
		show_text = 'Show Entire Page',
		hide_text = 'Hide Entire Page',
	},
	row = 'sb-allw sb-w%s-allg sb-w%s-g%s toggle-section-hidden'
}
local FOOTER_ITEMS = { 'Towers', 'Inhibitors', 'Barons', 'Dragons', 'RiftHeralds' }
local TIMEZONES = { 'PST', 'KST', 'CET' }
local sep = ','
local arg_sep = ';;;'
local lang = mw.getLanguage('en')
local h = {}
local p = {}
local s = {}

function p.button(frame)
	local args = util_args.merge(true)
	if util_args.castAsBool(args.name) then
		local section = util_vars.setGlobalIndex('sb_N_TabInPage_display', frame)
		if not util_args.castAsBool(args.notab) then
			util_vars.setGlobalIndex('sb_N_TabInPage', frame)
			util_vars.resetGlobalIndex('sb_N_GameInTab')
			util_vars.resetGlobalIndex('sb_N_MatchInTab')
			util_vars.setVar('sbTabName', args.name or section, frame)
		end
		util_toggle.prepDataByWeek(TOGGLES.tab, section)
		TOGGLES.tab.show_text = 'Show ' .. (args.display or args.name)
		TOGGLES.tab.hide_text = 'Hide ' .. (args.display or args.name)
		return util_toggle.printSectionToggler(nil, TOGGLES.tab), util_html.clear()
	else
		return util_toggle.printSectionToggler(nil, TOGGLES.all), util_html.clear()
	end
end

function p.header(frame)
	i18n.initGlobalFromFile('Scoreboard')
	local args = util_args.merge(true)
	local team1 = m_team.leftmediumlinked(args[1])
	local team2 = m_team.rightmediumlinked(args[2])
	local div = mw.html.create('div')
		:addClass('sb-title')
	util_html.vsAlign(team1, team2, nil, div)
	return div
end

function p.main(frame)
	i18n.initGlobalFromFile('Scoreboard')
	local args = util_args.merge(true)
	if util_args.castAsBool(args.notplayed) then
		return p._noGamePlayed(args)
	end
	SEASON = mw.loadData('Module:Scoreboard/SeasonData')[tonumber(args.season)]
	h.setSeasonVariables()
	h.initializePlayerN(args) -- in case it's not 5 players per team
	h.initializeItems(args)
	local player_data = h.getPlayerDisplayData(args)
	local game_data = h.getGameDisplayData(args)
	h.processCargoData(player_data, game_data, args)
	if h.doWeStoreCargo(args) then
		h.cargo(player_data, game_data)
	end
	return h.makeOutput(game_data, player_data)
end

function p._noGamePlayed(args)
	i18n.initGlobalFromFile('Scoreboard')
	local game_data = h.getNotPlayedGameData(args)
	h.prepToggles(game_data)
	local tbl = mw.html.create('table')
		:addClass('sb')
	h.printTeamLine(tbl, game_data)
	h.printScoreLine(tbl, game_data)
	h.printNotPlayedTitle(tbl, args)
	h.printNotPlayedReason(tbl, args)
	return tbl
end

function s.ChampionSprite(id)
	return Sprite{
		id,
		size = 60,
		type = 'Champion',
		nosize = true,
		notext = true,
		nolink = true
	}
end

function s.ChampionSprite2(id)
	local div = mw.html.create('div')
		:addClass('sb-footer-ban')
		:wikitext(Sprite{
			id,
			size = 30,
			type = 'Champion',
			nosize = true,
			notext = true,
			nolink = true
		})
	return tostring(div)
end

function s.SummonerSprite(id)
	return Sprite{
		id,
		size = 30,
		type = 'Summoner',
		notext = true,
		nolink = true
	}
end

function s.ItemSprite(id)
	return Sprite{
		id,
		size = 30,
		type = 'Item',
		nosize = true,
		notext = true,
		nolink = true
	}
end

function s.InfoSprite(id)
	return Sprite{
		id,
		size = 15,
		type = 'ScoreboardIcon',
		notext = true,
		nolink = true
	}
end

function h.setSeasonVariables()
	if not SEASON.rh then
		-- could use keyOf but meh
		table.remove(FOOTER_ITEMS, 5)
	end
end

function h.initializePlayerN(args)
	PLAYER_NUMBER = args.teamsize or PLAYER_NUMBER
end

function h.initializeItems(args)
	PRINT_ITEMS = not util_args.castAsBool(args.noitems)
end

function h.getPlayerDisplayData(args)
	local data = {}
	for i, side in ipairs(PLAYER_SIDES) do
		data[i] = {}
		for j = 1, PLAYER_NUMBER do
			local row = h.splitPlayerDataAndErrorcheck(args, side, i, j)
			data[i][j] = h.parsePlayerData(row, i, j)
		end
	end
	return data
end

function h.splitPlayerDataAndErrorcheck(args, side, i, j)
	if not args[side .. j] then
		error(i18n.print('no_player_data', i, j))
	end
	local row = util_args.splitArgs(args[side .. j], PLAYER_ARGS, arg_sep)
	return row
end

function h.parsePlayerData(row, i, j)
	row.Link = lang:ucfirst(row.Link or row.Name or '')
	row.Side = i
	row.Role_Number = j
	row.Role = m_role.rolename('scoreboard' .. j)
	row.Items = util_text.split(row.Items or '')
	row.SummonerSpells = util_text.split(row.SummonerSpells)
	row.Champion = m_champion.championname(row.Champion)
	return row
end

function h.getGameDisplayData(args)
	local game_data = {}
	h.setVariables(game_data, args)
	h.getWinnerAndLoser(game_data, args)
	h.getGameDataByRenamingArgs(game_data, args)
	h.getGamelengthNumber(game_data)
	h.getTeamDisplayData(game_data, args)
	h.getDateAndTime(game_data, args)
	h.getGameFootnotes(game_data, args)
	return game_data
end

function h.setVariables(game_data, args)
	local frame = mw.getCurrentFrame()
	game_data.N_MatchInTab = util_vars.setGlobalIndex('N_MatchInTab')
	game_data.N_MatchInPage = util_vars.setGlobalIndex('N_MatchInPage')
	game_data.N_GameInMatch = util_vars.setGlobalIndex('N_GameInMatch')
	game_data.Tournament = args.tournament
end

function h.getWinnerAndLoser(game_data, args)
	if args.winner and not tonumber(args.winner) then
		error('Invalid winner')
	elseif not args.winner then
		return
	end
	local winner = tonumber(args.winner)
	local loser = util_esports.otherTeamN(winner)
	game_data.Winner = winner
	game_data.WinTeam = m_team.teamlinkname(args['team' .. winner])
	game_data.LossTeam = m_team.teamlinkname(args['team' .. loser])
end

function h.getGameDataByRenamingArgs(game_data, args)
	for k, v in pairs(GAME_ARGS) do
		game_data[v] = args[k]
	end
	game_data.VOD = game_data.VOD or game_data.VOD2
end

function h.getGamelengthNumber(game_data)
	if not game_data.Gamelength then return end
	local m, s = game_data.Gamelength:match('(%d+):(%d+)')
	m = tonumber(m)
	s = tonumber(s)
	if not m then
		error("Can't find minutes in game length")
	elseif not s then
		error("Can't find seconds in game length")
	end
	game_data.Gamelength_Number = m + s / 60
end

function h.getTeamDisplayData(game_data, args)
	for i, s in ipairs(PLAYER_SIDES) do
		local key = 'Team' .. i
		local arg = 'team' .. i
		game_data[key] = m_team.teamlinkname(args[arg])
		game_data[key .. 'Bans'] = h.getBans(args, arg)
		h.getTeamDataByRenamingArgs(game_data, args, key, arg)
	end
end

function h.getBans(args, arg)
	local bans = util_args.numberedArgsToTable(args, arg .. 'ban', true, SEASON.max_bans) or {}
	util_table.mapInPlace(bans, m_champion.championname)
	util_table.padFalseEntries(bans, SEASON.max_bans, 'Loss of Ban')
	return bans
end

function h.getTeamDataByRenamingArgs(game_data, args, key, arg)
	for k, v in pairs(GAME_ARGS_TEAMS) do
		game_data[key .. v] = args[arg .. k]
	end
end

function h.getDateAndTime(game_data, args)
	local time_data = h.parseTimeData(args)
	game_data.tz = time_data
end

function h.parseTimeData(args)
	local dst = util_args.norm(args.dst)
	if args.time then
		return util_time.getTimezones(args.date .. ' ' .. args.time, args.timezone, dst)
	end
	for _, tz in ipairs(TIMEZONES) do
		if args[tz] then
			return util_time.getTimezones(args.date .. ' ' .. args[tz], tz, dst)
		end
	end
	return { UTC = 'Undefined' }
end

function h.getGameFootnotes(game_data, args)
	game_data.footnote = args.footnote
end

function h.processCargoData(player_data, game_data, args)
	game_data.cargo = h.initGameCargoFromData(game_data)
	game_data.cargo.OverviewPage = util_esports.getOverviewPage(args.page)
	game_data.cargo.N_MatchInTab = nil
	game_data.cargo.UniqueGame = util_cargo.getUniqueLine(game_data.N_MatchInPage, game_data.N_GameInMatch)
	game_data.cargo.UniqueLine = util_cargo.getUniqueLine(game_data.N_MatchInPage, game_data.N_GameInMatch)
	game_data.cargo.DateTime_UTC = game_data.tz.UTC
	-- TODO: Add list of picks to game maybe?
	-- add actual values to what's nil above
	h.addTeamCargoFieldsToGame(game_data.cargo, player_data)
	for t, team in ipairs(player_data) do
		for playerN, player_data in ipairs(team) do
			player_data.cargo = h.initPlayerCargoFromData(player_data)
			h.addPlayerCargoDataFromGame(player_data.cargo, game_data.cargo, t)
			h.addPlayerCargoRoleData(player_data.cargo, playerN)
			h.addPlayerCargoKeystoneData(player_data.cargo, player_data)
			player_data.cargo.Side = t
			player_data.cargo.UniqueLine = util_cargo.getUniqueLine(game_data.N_MatchInPage, game_data.N_GameInMatch, t, playerN)
			player_data.cargo.UniqueLineVs = util_cargo.getUniqueLine(game_data.N_MatchInPage, game_data.N_GameInMatch, util_esports.otherTeamN(t), playerN)
		end
	end
end

function h.initGameCargoFromData(game_data)
	local fields = { 'Tournament', 'Team1', 'Team2', 'WinTeam', 'LossTeam', 'DST', 'Team1Score', 'Team2Score', 'Winner', 'Gamelength', 'Gamelength_Number', 'Team1Bans', 'Team2Bans', 'Team1Dragons', 'Team2Dragons', 'Team1Barons', 'Team2Barons', 'Team1Towers', 'Team2Towers', 'Team1Gold', 'Team2Gold', 'Team1Kills', 'Team2Kills', 'Team1RiftHeralds', 'Team2RiftHeralds', 'Team1Inhibitors', 'Team2Inhibitors', 'Patch', 'MatchHistory', 'Gamename', 'N_GameInMatch', 'N_MatchInPage', 'VOD' }
	local ret = { _table = 'ScoreboardGame' }
	for _, v in ipairs(fields) do
		ret[v] = h.castForCargo(game_data[v])
	end
	return ret
end

function h.addTeamCargoFieldsToGame(cargo, player_data)
	cargo.Team1Picks = h.getTeamDataForCargo(player_data[1], 'Champion')
	cargo.Team2Picks = h.getTeamDataForCargo(player_data[2], 'Champion')
	cargo.Team1Links = h.getTeamDataForCargo(player_data[1], 'Link')
	cargo.Team2Links = h.getTeamDataForCargo(player_data[2], 'Link')
	cargo.Team1Names = h.getTeamDataForCargo(player_data[1], 'Name')
	cargo.Team2Names = h.getTeamDataForCargo(player_data[2], 'Name')
end

function h.getTeamDataForCargo(team, datapoint)
	local tbl = {}
	for _, player in ipairs(team) do
		tbl[#tbl+1] = player[datapoint]
	end
	return util_table.concat(tbl, ',')
end

function h.initPlayerCargoFromData(player_data)
	local fields = { 'Name', 'Link', 'Champion', 'Kills', 'Deaths', 'Assists', 'SummonerSpells', 'Gold', 'CS', 'Items', 'Trinket' }
	local ret = { _table = 'ScoreboardPlayer' }
	for _, v in ipairs(fields) do
		ret[v] = h.castForCargo(player_data[v])
	end
	return ret
end

function h.castForCargo(v)
	if type(v) == 'table' then
		return util_table.concat(v)
	end
	return v
end

function h.addPlayerCargoDataFromGame(player, game, team)
	player.Team = game['Team' .. team]
	player.TeamVs = game['Team' .. util_esports.otherTeamN(team)]
	player.TeamGold = game['Team' .. team .. 'Gold']
	player.TeamKills = game['Team' .. team .. 'Kills']
	player.DateTime_UTC = game.DateTime_UTC
	player.OverviewPage = game.OverviewPage
	player.UniqueGame = game.UniqueLine
	player.PlayerWin = game.Winner == team
end

function h.addPlayerCargoRoleData(cargo, playerN)
	cargo.Role = m_role.storename('scoreboard' .. playerN)
	cargo.Role_Number = playerN
end

function h.addPlayerCargoKeystoneData(cargo, player_data)
	if not SEASON.keystone then return end
	if SEASON.keystone == 'Mastery' then
		cargo.KeystoneMastery = player_data.Keystone
	elseif SEASON.keystone == 'Rune' then
		cargo.KeystoneRune = player_data.Keystone
	end
end

-- cargo
function h.doWeStoreCargo(args)
	if util_args.castAsBool(args.nocargo) then
		return false
	end
	return mw.title.getCurrentTitle().nsText == ''
end

function h.cargo(player_data, game_data)
	util_cargo.store(game_data.cargo)
	for _, team in ipairs(player_data) do
		for _, player in ipairs(team) do
			util_cargo.store(player.cargo)
		end
	end
end
-- output
function h.makeOutput(game_data, player_data)
	h.prepToggles(game_data)
	local tbl = mw.html.create('table')
		:addClass('sb')
	h.printTeamLine(tbl, game_data)
	h.printScoreLine(tbl, game_data)
	h.printHeaderLine(tbl, game_data)
	h.printLine(tbl, game_data, 'th', h.printKey)
	h.printLine(tbl, player_data, 'td', h.printPlayers)
	h.printDateTimeAndLinks(tbl, game_data)
	h.printLine(tbl, game_data, 'td', h.printFooter)
	return tbl
end

function h.prepToggles(game_data)
	util_toggle.prepDataByWeekAndGame(TOGGLES.one, game_data.N_MatchInPage, game_data.N_GameInMatch)
	TOGGLES.row = TOGGLES.row:format(game_data.N_MatchInPage, game_data.N_MatchInPage, game_data.N_GameInMatch)
end

function h.printTeamLine(tbl, game_data)
	local tr = tbl:tag('tr')
	local th1 = tr:tag('th')
		:addClass('sb-teamname')
		:wikitext(m_team.rightmediumlinked(game_data.Team1))
	h.printVs(tr, game_data)
	local th2 = tr:tag('th')
		:addClass('sb-teamname')
		:wikitext(m_team.leftmediumlinked(game_data.Team2))
end

function h.printVs(tr, game_data)
	local thVs = tr:tag('th')
		:addClass('sb-teamname-vs')
		:attr('colspan',2)
		:wikitext('vs')
	util_footnote.tagFootnotePlain(thVs, game_data.footnote)
end

function h.printScoreLine(tbl, game_data)
	local tr = tbl:tag('tr')
	h.printScore(tr, game_data.Team1Score, 'blue', game_data.Winner == 1)
	local th = tr:tag('th'):attr('colspan','2')
	util_toggle.printToggleButton(th, TOGGLES.one)
	h.printScore(tr, game_data.Team2Score, 'red', game_data.Winner == 2)
end

function h.printScore(tr, score, side, isWinner)
	local th = tr:tag('th')
		:addClass('side-' .. side)
		:wikitext(score)
	if isWinner then
		th:addClass('sb-score-winner')
	end
end

function h.printHeaderLine(tbl, game_data)
	local tr = tbl:tag('tr')
		:addClass(TOGGLES.row)
	h.printHeaderCell(tr, game_data, 1)
	h.printGameLength(tr, game_data)
	h.printHeaderCell(tr, game_data, 2)
end

function h.printHeaderCell(tr, game_data, i)
	local td = tr:tag('th')
	if i == 2 then
		td:addClass('side-red')
	end
	local div = td:tag('div')
		:addClass('sb-header')
	local verdict
	if game_data.Winner == i then
		verdict = 'Victory'
	elseif game_data.Winner == util_esports.otherTeamN(i) then
		verdict = 'Defeat'
	end
	h.div(div, 'header-vertict', verdict)
	h.printHeaderInfo(div, 'Gold', util_esports.roundedGold(game_data['Team' .. i .. 'Gold']))
	h.printHeaderInfo(div, 'Kills', game_data['Team' .. i .. 'Kills'])
end

function h.printHeaderInfo(parent, name, wikitext)
	local class = ('%s'):format(lang:lc(name))
	parent:tag('div')
		:addClass('sb-header-' .. name)
		:attr('title', i18n.print(name))
		:wikitext(s.InfoSprite(name), ' ', wikitext)
end

function h.printGameLength(tr, game_data)
	local th = tr:tag('th')
		:attr('colspan', 2)
	th:wikitext(game_data.Gamelength)
end

function h.printLine(tbl, data, celltype, f)
	local tr = tbl:tag('tr')
		:addClass(TOGGLES.row)
	for i, side in ipairs(TEAMS) do
		local td = tr:tag(celltype)
			:attr('colspan',2)
			:addClass('side-' .. side)
		f(td, data, i)
	end
end

function h.div(parent, class, wikitext, extra)
	local div = parent:tag('div'):addClass('sb-' .. class):wikitext(wikitext)
	if extra then
		div:addClass('sb-' .. class .. '-' .. extra)
	end
end

function h.printKey(td)
	-- honestly it's just not worth it to write any kind of loop for this
	local outer = td:tag('div'):addClass('sb-key')
	h.div(outer, 'key-champion', 'Champ')
	h.div(outer, 'key-summoners', 'SS')
	if SEASON.runes then
		h.div(outer, 'key-runes', 'R')
	end
	local info = outer:tag('div'):addClass('sb-key-info')
	local stats = info:tag('div'):addClass('sb-key-stats')
	h.div(stats, 'key-stat', 'KDA', 'kda')
	h.div(stats, 'key-stat', 'CS', 'cs')
	h.div(stats, 'key-stat', 'Gold', 'gold')
	if SEASON.trinket then
		h.div(stats, 'key-trinket', 'T')
	end
	if PRINT_ITEMS then
		h.div(outer, 'key-items', 'Items')
	end
end

function h.printPlayers(td, player_data, i)
	local data = player_data[i]
	for _, row in ipairs(data) do
		h.printPlayer(td, row)
	end
end

function h.printPlayer(td, row)
	local outer = td:tag('div'):addClass('sb-p')
	h.div(outer, 'p-champion', s.ChampionSprite(row.Champion))
	h.printPlayerSummoners(outer, row.SummonerSpells)
	if SEASON.runes then
		h.printPlayerRunes(outer, row)
	end
	local info = outer:tag('div'):addClass('sb-p-info')
	h.div(info, 'p-name', util_text.intLink(row.Link, row.Name))
	h.printPlayerStats(info, row)
	if SEASON.mastery then
		h.printTrinketAndMastery(outer, row)
	end
	if PRINT_ITEMS then
		h.printPlayerItems(outer, row.Items)
	end
end

function h.printPlayerSummoners(outer, spells)
	local ss = outer:tag('div'):addClass('sb-p-summoners')
	h.div(ss, 'p-sum', s.SummonerSprite(spells[1]))
	h.div(ss, 'p-sum', s.SummonerSprite(spells[2]))
end

function h.printPlayerRunes(outer, row)
	local runes = outer:tag('div'):addClass('sb-p-runes')
	h.printPlayerRune(runes, row.Keystone or 'EmptySummoner')
	h.printPlayerRune(runes, row.Secondary or 'EmptySummoner')
end

function h.printPlayerRune(runes_div, rune)
	h.div(runes_div, 'p-rune', ('[[File:Rune %s.png|30px|link=]]'):format(rune))
end

function h.printTrinketAndMastery(outer, row)
	local inner = outer:tag('div'):addClass('sb-p-masteryandtrinket')
	h.div(inner, 'p-trinket', ('[[File:Mastery %s.png|30px|link=]]'):format(row.Keystone or 'None'))
	h.printTrinket(inner, row.Trinket)
end

function h.printTrinket(div, trinket)
	h.div(div, 'p-trinket', s.ItemSprite(trinket))
end

function h.printPlayerStats(info, row)
	local stats = info:tag('div'):addClass('sb-p-stats')
	h.div(stats, 'p-stat', util_esports.KDA(row.Kills or '', row.Deaths or '', row.Assists or ''), 'kda')
	h.div(stats, 'p-stat', row.CS, 'cs')
	h.div(stats, 'p-stat', util_esports.roundedGold(row.Gold), 'gold')
	if SEASON.trinket and not SEASON.mastery then
		h.printTrinket(stats, row.Trinket)
	end
end

function h.printPlayerItems(outer, items)
	h.div(outer, 'p-items', util_table.concat(items, '', s.ItemSprite))
end

function h.printDateTimeAndLinks(tbl, game_data)
	local tr = tbl:tag('tr')
		:addClass(TOGGLES.row)
	local td = tr:tag('td')
		:attr('colspan',4)
		:addClass('sb-datetime-outer')
		:addClass('plainlinks')
	local div = td:tag('div')
		:addClass('sb-datetime')
	h.printDateAndTime(div, game_data.tz.UTC)
	h.printPatch(div, game_data.Patch)
	h.printMH(div, game_data.MatchHistory)
	h.printVOD(div, game_data.VOD)
end

function h.printDateAndTime(div, utc)
	local date = util_time.dateInLocal(utc)
	local time = util_time.timeInLocal(utc)
	local display = date .. ' ' .. time
	h.printDateTimeItem(div, 'date', display, 'Date & Time of the match in your local time zone')
end

function h.printPatch(div, patch)
	local display = patch and util_text.intLinkOrText('Patch ' .. patch) or ''
	h.printDateTimeItem(div, 'patch', display, 'Patch')
end

function h.printMH(div, mh)
	local display = i18n.print('match_history') .. (util_text.extLink(mh, 'Link') or 'N/A')
	h.printDateTimeItem(div, 'mh', display)
end

function h.printVOD(div, vod)
	local display = i18n.print('vod') .. (util_text.extLink(vod, 'Link') or 'N/A')
	h.printDateTimeItem(div, 'vod', display)
end

function h.printDateTimeItem(div, class, str, title)
	div:tag('div')
		:addClass('sb-datetime-' .. class)
		:wikitext(str)
		:attr('title', title)
end

function h.printFooter(td, game_data, i)
	local key = 'Team' .. i
	local div = td:tag('div'):addClass('sb-footer')
	local bans = div:tag('div'):addClass('sb-footer-bans')
	h.div(bans, 'footer-ban-header', nil)
	h.div(bans, 'footer-bans', util_table.concat(game_data[key .. 'Bans'], '', s.ChampionSprite2))
	local stats = div:tag('div'):addClass('sb-footer-stats')
	for _, v in ipairs(FOOTER_ITEMS) do
		h.printFooterInfo(stats, game_data, key, v, 'footer-item')
	end
end

function h.printFooterInfo(parent, data, key, datapoint, parentclass)
	local fullkey = key .. datapoint
	local class2 = ('%s'):format(lang:lc(datapoint))
	parent:tag('div')
		:addClass('sb-' .. parentclass)
		:addClass('sb-' .. parentclass .. '-' .. class2)
		:wikitext(s.InfoSprite(fullkey), ' ', data[fullkey])
		:attr('title', i18n.print(datapoint))
end
-- if game wasn't played
function h.getNotPlayedGameData(args)
	local game_data = {}
	h.setVariables(game_data, args)
	h.getWinnerAndLoser(game_data, args)
	h.getGameDataByRenamingArgs(game_data, args)
	h.getNotPlayedTeamDisplayData(game_data, args)
	h.getDateAndTime(game_data, args)
	h.getGameFootnotes(game_data, args)
	return game_data
end

function h.getNotPlayedTeamDisplayData(game_data, args)
	for i, s in ipairs(PLAYER_SIDES) do
		local key = 'Team' .. i
		local arg = 'team' .. i
		game_data[key] = m_team.teamlinkname(args[arg])
		h.getTeamDataByRenamingArgs(game_data, args, key, arg)
	end
end

function h.printNotPlayedTitle(tbl, args)
	local tr = tbl:tag('tr')
	tr:tag('td')
		:addClass('sb-notplayed-header')
		:addClass(TOGGLES.row)
		:attr('colspan', 4)
		:wikitext(args.notplayed)
end

function h.printNotPlayedReason(tbl, args)
	local tr = tbl:tag('tr')
	tr:tag('td')
		:addClass('sb-notplayed')
		:addClass(TOGGLES.row)
		:attr('colspan', 4)
		:wikitext(args.reason or i18n.print('no_reason'))
end
return p