Hello there! We are conducting a survey to better understand the user experience in making a first edit. If you have ever made an edit on Gamepedia, please fill out the survey. Thank you!

Module:MatchList

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

To edit the documentation or categories for this module, click here. This module has an i18n file. Click here to edit it.

This module adds the following hooks:
  • onMatchListProcessTabRow
  • onMatchListPrintTogglesEnd
  • onMatchListPrintCaption
This module runs and makes the following hooks available:
  • onMatchListProcessTabRow
  • onMatchListProcessNetData
  • onMatchListPrintTogglesEnd
  • onMatchListPrintCaption

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_game = require('Module:GameUtil')
local util_html = require('Module:HTMLUtil')
local util_matches = require('Module:MatchesUtil')
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_team = require('Module:Team')
local i18n = require('Module:i18nUtil')
local Hook = require('Module:Hook')

local lang = mw.getLanguage('en')

local TZ = { 'CD', 'You', 'PST', 'CET', 'KST' }
local NORMAL_TZ = { 'PST', 'CET', 'KST' }

local HIDDENCLASS = 'matches-hiddentab'

local TAB_TOGGLES = {
	all = {
		show_attr = '.ml-allw',
		hide_attr = '.ml-btn',
		show_class = 'ml-allw',
		hide_class = 'ml-btn',
		show_id = 'matchlist-show-all',
		hiddenclass = HIDDENCLASS,
	},
	week = {
		show_attr = '.ml-w%s',
		hide_attr = '.ml-btn%s',
		hiddenclass = HIDDENCLASS,
		show_class = 'ml-allw ml-w%s',
		hide_class = 'ml-btn ml-btn%s',
	},
	row = 'ml-allw ml-w%s %s',
	daterange = 'ml-btn ml-btn%s %s',
}

local WIDTHS = { 110, 25, 25, 110 }

local RS_TOGGLES = {
	hiddenclass = 'matchlist-rs-hidden',
	order = { 'res', 'sch' },
	attrs = {
		id = {
			sch = 'matchlist-show-schedule'
		}
	}
}

local TZ_TOGGLES = {
	init = 'You',
	order = { 'CD', 'You', 'PST', 'CET', 'KST' },
	attrs = {
		title = {
			CD = i18n.print('Countdown_title'),
			You = i18n.print('You_title'),
			PST = i18n.print('PST_title'),
			CET = i18n.print('CET_title'),
			KST = i18n.print('KST_title')
		},
	}
}

local HASFLEX = false
local THIS = nil

local h = {}
local p = {}
function p.main(frame)
	local args = util_args.merge(true)
	h.addWikiSpecificHooks()
	return p._main(args)
end

function p._main(args)
	i18n.initGlobalFromFile('MatchList')
	local matchData = p._getMatchData(util_esports.getOverviewPage(args.page), args)
	return p._makeOutput(matchData, args)
end

function p.printSingleTab(args)
	local args = util_args.merge(true)
	local overviewPage = util_esports.getOverviewPage(args.page)
	local data_tab = p._getMatchData(overviewPage, args)
	local tbl_tab = mw.html.create('table')
		:addClass('wikitable')
		:addClass('matchlist')
	util_html.makeColspanHeader(tbl_tab, args.display, 4)
	util_html.makeEmptyWidthRowPX(tbl_tab, WIDTHS)
	local lastgroup
	for k, row in ipairs(data_tab[1] or {}) do
		if row.innergroup ~= lastgroup then
			tbl_tab:tag('tr'):addClass('matchlist-date')
				:tag('td'):attr('colspan',4):wikitext(row.innergroup)
			lastgroup = row.innergroup
		end
		local tr = tbl_tab:tag('tr')
		h.printTeam1(tr, row)
		h.printScore(tr, row, true)
		h.printTeam2(tr, row)
	end
	return tbl_tab
end

function h.addWikiSpecificHooks()
	Hook.add('onMatchListPrintCaption', util_game.onMatchListPrintCaption)
	Hook.add('onMatchListProcessTabRow', util_game.onMatchListProcessTabRow)
	Hook.add('onMatchListPrintTogglesEnd', util_game.onMatchListPrintTogglesEnd)
end

-- match cargo
function p._getMatchData(page, args)
	local outergroup = args.outergroup or 'Tab'
	local innergroup = args.innergroup or 'Date'
	local matchResult = util_cargo.queryAndCast(h.makeMatchQuery(page, args))
	local matchData = util_cargo.groupResultOrdered(matchResult, outergroup)
	for i, tab in ipairs(matchData) do
		tab.index = i
		for j, row in ipairs(tab) do
			row.index = j
			h.processMatchRow(row, i, j, args, tab)
		end
	end
	Hook.run('onMatchListProcessNetData', matchData)
	THIS = util_matches.determineThis(matchData, args.This)
	return matchData
end

function h.makeMatchQuery(page, args)
	local query = {
		tables = 'MatchSchedule',
		fields = h.makeMatchFields(args),
		where = h.makeMatchWhere(page, args),
		orderBy = 'N_Page ASC, N_TabInPage ASC, N_MatchInTab ASC',
		types = {
			Winner = 'number',
			Team1Score = 'number',
			Team2Score = 'number',
			FF = 'number',
			N_TabInPage = 'number',
			HasTime = 'boolean'
		},
		limit = 9999,
		groupBy = 'UniqueMatch',
	}
	return query
end

function h.makeMatchFields(args)
	local tbl = {
			'Team1',
			'Team2',
			'Team1Final',
			'Team2Final',
			'Winner',
			'Team1Score',
			'Team2Score',
			'Patch',
			'Disabled',
			'Hotfix',
			'PatchFootnote',
			'FF',
			'Tab',
			'N_TabInPage',
			'UniqueMatch',
			'DateTime_UTC=UTC',
			'DST',
			'HasTime',
			'OverviewPage',
			'MatchDay',
			'Stream',
			'IsFlexibleStart',
			'BestOf',
			'InitialN_MatchInTab',
			'InitialPageAndTab',
			'CONCAT(N_Page,"_",N_TabInPage)=PageAndTab',
		}
	if not util_args.castAsBool(args.nofootnotes) then
		util_table.mergeArrays(tbl, { 'Team1Footnote', 'Team2Footnote', 'Footnote' })
	end
	return tbl
end

function h.makeMatchWhere(page, args)
	local tbl = {
		('OverviewPage="%s"'):format(page),
		util_cargo.whereFromArg('Tab="%s"', args.onlytab)
	}
	if args.team then
		local teamlink = m_team.teamlinkname(args.team)
		tbl[#tbl+1] = ('(Team1Final="%s" OR Team2Final="%s")'):format(teamlink, teamlink)
	end
	return util_cargo.concatWhere(tbl)
end

function h.processMatchRow(row, i, j, args, tab)
	h.processDateTime(row, i, j, h.lastRow(tab, j))
	h.processFF(row)
	row.innergroup = args.innergroup and row[args.innergroup]
	row.NewDay = h.nextRow(tab, j).MatchDay and h.nextRow(tab, j).MatchDay ~= row.MatchDay
	row.TimestampAttr = h.getTimestampAttr(row, tab, j)
	HASFLEX = HASFLEX or util_args.castAsBool(row.IsFlexibleStart)
	Hook.run('onMatchListProcessTabRow', row, i, j, args, tab)
end

function h.lastRow(tab, j)
	return tab[j-1] or { NewDay = true }
end

function h.nextRow(tab, j)
	return tab[j+1] or {}
end

function h.processDateTime(row, i, j, lastrow)
	if row.UTC then
		h.processDateTimeKnown(row, i, j, lastrow)
	else
		h.setDatesTBD(row)
	end
end

function h.processDateTimeKnown(row, i, j, lastrow)
	util_time.addTimezonesToRowFromUTC(row)
	for _, tz in ipairs(NORMAL_TZ) do
		row[tz .. '_Time'] = row[tz]:match('(%d%d:%d%d)')
		
		local date = lang:formatDate('D Y-m-d',row[tz])
		row[tz .. '_Date'] = date
		row[tz .. '_isNewDate'] = date ~= lastrow[tz .. '_Date']
	end
	if row.HasTime then
		h.setTimesYou(row, row.UTC, i, j)
	else
		h.setTimesTBD(row)
	end
end

function h.setTimesYou(row, UTC, i, j)
	row.You_Time = util_time.timeInLocal(UTC)
	row.CD_Time = util_time.countdown(UTC, h.getCountdownSettings(row, i, j))
	row.You_Date = util_time.dateInLocal(UTC)
	row.You_Date_Range = util_time.dateInLocalMatches(UTC)
end

function h.getCountdownSettings(row, i, j)
	if row.Stream then
		return {
			options = { 'matches-format', 'no-leading-zeros' },
			data_end = 'toggle',
			default = '<span class="matchlist-streamlink-icon"></span>',
			i = ('%s_%s'):format(i, j)
		}
	else
		return {
			options = { 'matches-format', 'no-leading-zeros' }
		}
	end
end

function h.setTimesTBD(row)
	for _, tz in ipairs(TZ) do
		row[tz .. '_Time'] = 'TBD'
	end
	row.You_Date = 'TBD'
	row.You_Date_Range = 'TBD'
	return
end

function h.setDatesTBD(row)
	for _, tz in ipairs(TZ) do
		row[tz .. '_Date'] = 'TBD'
	end
	row.You_Date = 'TBD'
	row.You_Date_Range = 'TBD'
	h.setTimesTBD(row)
end

function h.processFF(row)
	if not row.FF then return end
	row.Team1Score = row.FF == 2 and 'W' or 'FF'
	row.Team2Score = row.FF == 1 and 'W' or 'FF'
end

function h.getTimestampAttr(row, tab, j)
	if h.lastRow(tab, j).NewDay then
		return util_time.unixNumber(row.UTC)
	else
		return h.lastRow(tab, j).TimestampAttr
	end
end

-- print
function p._makeOutput(data, args)
	util_footnote.initializeAllFootnotes()
	local output = mw.html.create('div'):attr('id','matchlist')
	if HASFLEX then
		h.printFlexNote(output)
	end
	h.printToggles(output)
	h.printAllTabs(output, data, args)
	util_footnote.printFootnotes(output)
	return output
end

function h.printToggles(tbl)
	util_toggle.printSectionToggler(tbl, TAB_TOGGLES.all)
	h.printResultsToggle(tbl)
	h.printTZToggle(tbl)
	h.printPredictionsButton(tbl)
	Hook.run('onMatchListPrintTogglesEnd', tbl)
	tbl:tag('div')
			:css('clear','left')
			:attr('id', 'matchlist-section-start')
	return
end

function h.printResultsToggle(tbl)
	local div = tbl:tag('div')
		:addClass('toggle-button')
	RS_TOGGLES.displays = {
		res = i18n.print('results_init'),
		sch = i18n.print('results')
	}
	util_toggle.printOptionFromListTogglers(div, RS_TOGGLES)
end

function h.printTZToggle(tbl)
	local div = tbl:tag('div'):addClass('toggle-button')
	util_toggle.printOptionFromListTogglers(div, TZ_TOGGLES)
	div:wikitext('|')
	util_html.printHelpText(div, i18n.print('helptext'))
end

function h.printPredictionsButton(tbl)
	if not THIS then return end
	local div = tbl:tag('div')
		:addClass('toggle-button')
		:addClass('prediction-action')
		:attr('id', 'matches-prediction-begin')
		:wikitext(i18n.print('launch_predictions'))
end

function h.printFlexNote(tbl)
	tbl:wikitext(mw.getCurrentFrame():expandTemplate{title='FlexNotice',args={''}})
end

function h.printAllTabs(output, data, args)
	local div = output:tag('div')
		:attr('id', 'matchlist-content-wrapper')
		:addClass('ml-normal-pred-and-results')
		:addClass(args.tabwrapperclass)
	for i, tab in ipairs(data) do
		h.printTab(div, tab, THIS == i)
	end
end

function h.printTab(div, tab, isfocused)
	local innerDiv = div:tag('div')
		:addClass('matchlist-tab-wrapper')
	local tbl = innerDiv:tag('table')
		:addClass('wikitable')
		:addClass('matchlist')
	h.printPatchCaption(tbl, tab)
	h.printTabHeader(tbl, tab.index, tab.name, isfocused)
	h.printDateRange(
		tbl,
		tab,
		h.getDateRangeToggleClass(tab.index, isfocused)
	)
	util_html.makeEmptyWidthRowPX(tbl, WIDTHS)
	local toggle_class = h.getRowToggleClass(tab.index, isfocused)
	for k, row in ipairs(tab) do
		h.printFullRow(tbl, row, toggle_class, k == 1)
	end
	h.printPredictionTotals(innerDiv, tab.predictionTotals)
	return
end

function h.printPatchCaption(tbl, tab)
	Hook.run('onMatchListPrintCaption', tbl, tab)
end

function h.printTabHeader(tbl, i, name, isfocused)
	local data = mw.clone(TAB_TOGGLES.week)
	util_toggle.prepDataByWeek(data, i)
	data.initshown = isfocused
	util_toggle.printToggleHeader(tbl, 4, name, data)
	return
end

function h.printDateRange(tbl, tab_data, daterowclass)
	local tr = tbl:tag('tr')
		:addClass(daterowclass)
	local td = tr:tag('td')
		:attr('colspan',4)
	local n = #tab_data
	for _, tz in ipairs(NORMAL_TZ) do
		h.printNormalTZDatesToRangeCell(
			td:tag('span'),
			tz,
			tab_data[1][tz .. '_Date'],
			tab_data[n][tz .. '_Date']
		)
	end
	h.printYouTZDatesToRangeCell(
		td:tag('span'),
		tab_data[1].You_Date_Range,
		tab_data[n].You_Date_Range
	)
end

function h.printNormalTZDatesToRangeCell(span, tz, text1, text2)
	text1 = text1 ~= 'TBD' and lang:formatDate('D j M', text1) or text1
	text2 = text2 ~= 'TBD' and lang:formatDate('D j M', text2) or text2
	span:addClass('matchlist-daterange') -- for css, toggle displays handled through util below
		:wikitext(text1)
	if text2 ~= text1 then
		span:wikitext(' - ')
		span:wikitext(text2)
	end
	util_toggle.oflCellClasses(span, TZ_TOGGLES, tz)
	return
end

function h.printYouTZDatesToRangeCell(span, text1, text2)
	-- this is a special case because we need extra classes for JS to do its thing
	-- because we don't know if the dates are the same or not
	-- given that this is already special, we may as well do You and CD as one instead of splitting
	util_toggle.oflCellClasses(span, TZ_TOGGLES, 'You')
	span:addClass('matchlist-daterange')
		:addClass('matchlist-daterange-you')
		:addClass(TZ_TOGGLES.classes.CD) -- manually add this one because it's the same as YOU
	span:tag('span')
		:addClass('matchlist-daterange-you-1')
		:wikitext(text1)
	span:tag('span')
		:addClass('matchlist-daterange-you-hyphen')
		:wikitext(' - ')
	span:tag('span')
		:addClass('matchlist-daterange-you-hyphen')
		:wikitext(text2)
	return
end

function h.getDateRangeToggleClass(i, isfocused)
	return TAB_TOGGLES.daterange:format(i, not isfocused and '' or HIDDENCLASS)
end

function h.getRowToggleClass(i, isfocused)
	return TAB_TOGGLES.row:format(i, isfocused and '' or HIDDENCLASS)
end

function h.printFullRow(tbl_tab, row, toggle_class, isfirst)
	h.printDateRow(tbl_tab, row, toggle_class, isfirst)
	local tr = tbl_tab:tag('tr')
		:addClass(toggle_class)
		:addClass('ml-row')
		:attr('data-initial-order', row.InitialN_MatchInTab)
		:attr('data-initial-pageandtab', row.InitialPageAndTab)
	h.tagRowIfOver(tr, row)
	h.addDateAttr(tr, row)
	h.addNewDay(tr, row)
	h.addFlexStart(tr, row)
	util_matches.printCustomClass(tr, row)
	h.printTeam1(tr, row)
	h.printMiddle(tr, row)
	h.printTeam2(tr, row)
end

function h.printDateRow(tbl, row, toggle_class, isfirst)
	for _, tz in ipairs(NORMAL_TZ) do
		if row[tz .. '_isNewDate'] then
			local tr = tbl:tag('tr')
				:addClass(toggle_class)
			h.printNormalTZDateToRow(tr, tz, row[tz .. '_Date'])
		end
	end
	local tr = tbl:tag('tr')
		:addClass(toggle_class)
	h.printYouTZDateToRow(tr, row.You_Date)
	if isfirst then
		tr:attr('data-isfirst','yes')
	end
	return
end

function h.printNormalTZDateToRow(tr, tz, text)
	tr:addClass('matchlist-date')
	util_toggle.oflCellClasses(tr, TZ_TOGGLES, tz)
	local td = tr:tag('td')
				:attr('colspan','4')
	td:wikitext(text)
	return
end

function h.printYouTZDateToRow(tr, text)
	tr:addClass('matchlist-date')
		:addClass('matchlist-you-date') -- to select and remove the 'You' rows
		:addClass(TZ_TOGGLES.classes.CD)
	util_toggle.oflCellClasses(tr, TZ_TOGGLES, 'You')
	local td = tr:tag('td')
		:attr('colspan','4')
	td:wikitext(text)
	return td
end

function h.tagRowIfOver(tr, row)
	if not h.isOver(row) then
		tr:addClass('ml-row-tbd')
			:attr('data-prediction-expire', row.TimestampAttr)
	end
end

function h.isOver(row)
	return (row.Team1Score and row.Team2Score) or row.Winner
end

function h.addDateAttr(tr, row)
	if not row.UTC then return end
	tr:attr('data-date',util_time.strToDateStr(row.UTC))
end

function h.addNewDay(tr, row)
	if not row.NewDay then return end
	tr:addClass('matchlist-newday')
end

function h.addFlexStart(tr, row)
	if not util_args.castAsBool(row.IsFlexibleStart) then return end
	tr:addClass('matchlist-flex')
end

function h.printTeam1(tr, row)
	if not row.Team1 or row.Team1 == 'TBD' then tr:addClass('ml-row-unknown-team') end
	local td = tr:tag('td')
		:addClass('matchlist-team1')
		:addClass('ml-team')
	util_esports.addTeamHighlighter(td, row.Team1Final)
	util_footnote.tagFootnote(td, row.Team1Footnote)
	td:wikitext(m_team.leftshort(row.Team1, {link=''}))
	if row.Winner == 1 then
		td:addClass('matchlist-winner-team')
	elseif row.Winner == 0 then
		td:addClass('matchlist-tied-team')
	end
	return
end

function h.printTeam2(tr, row)
	if not row.Team2 or row.Team2 == 'TBD' then tr:addClass('ml-row-unknown-team') end
	local td = tr:tag('td')
		:addClass('matchlist-team2')
		:addClass('ml-team')
	util_esports.addTeamHighlighter(td, row.Team2Final)
	td:wikitext(m_team.rightshort(row.Team2, {link=''}))
	util_footnote.tagFootnotes(td, h.team2Footnotes(row))
	if row.Winner == 2 then
		td:addClass('matchlist-winner-team')
	elseif row.Winner == 0 then
		td:addClass('matchlist-tied-team')
	end
	return
end

function h.team2Footnotes(row)
	local tbl = { row.Team2Footnote, row.Footnote }
	util_table.removeFalseEntries(tbl, 2)
	return tbl
end

function h.printMiddle(tr, row)
	if h.isOver(row) then
		h.printScore(tr, row)
		h.printTime(tr, row, true)
	else
		h.printTime(tr, row)
	end
end

function h.printScore(tr, row, suppresstoggle)
	local td1 = h.printOneScore(tr, row.Team1Score, row.Team1Final)
	local td2 = h.printOneScore(tr, row.Team2Score, row.Team2Final)
	if row.Winner == 1 then
		td1:addClass('matchlist-winner-score')
	elseif row.Winner == 2 then
		td2:addClass('matchlist-winner-score')
	elseif row.Winner == 0 then
		td1:addClass('matchlist-tied-score')
		td2:addClass('matchlist-tied-score')
	end
	if not suppresstoggle then
		util_toggle.oflCellClasses(td1, RS_TOGGLES, 'res')
		util_toggle.oflCellClasses(td2, RS_TOGGLES, 'res')
	end
	return
end

function h.printOneScore(tr, score, team)
	local td = tr:tag('td'):wikitext(score)
		:addClass('matchlist-score')
	util_esports.addTeamHighlighter(td, team)
	return td
end

function h.printTime(tr, row, usetoggle)
	local td = tr:tag('td')
		:attr('colspan','2')
		:addClass('matchlist-time-cell')
		:addClass('plainlinks')
	if usetoggle then
		util_toggle.oflCellClasses(td, RS_TOGGLES, 'sch')
	end
	for _, tz in ipairs(TZ) do
		local span = td:tag('span')
		if row.Stream then
			span:wikitext('[', row.Stream, ' ', row[tz .. '_Time'], ']')
		else
			span:wikitext(row[tz .. '_Time'])
		end
		util_toggle.oflCellClasses(span, TZ_TOGGLES, tz)
	end
	return
end

function h.printPredictionTotals(div, totals)
	if not totals then return end
	local inner = div:tag('div')
		:addClass('ml-user-prediction-totals')
	inner:wikitext(('Correct: %s/%s (%s Guessed)'):format(totals.right, totals.over, totals.made))
end

return p