Module:MLB standings

-- This module copies content from Template:MLB_standings; see the history of that page -- for attribution.

local me = { }

local mlbData = mw.loadData('Module:MLB standings/data') local Navbar = require('Module:Navbar')

-- -- defaultOutputForInput: table mapping from input format to default output format --  (if the output format is not specified in the template arguments) -- local defaultOutputForInput = { default = 'default', overallWinLoss = 'winLossOnly', }

-- -- readTeamInfo: table of input parsers --  Keys are the input formats, values are functions that parse the unnamed parameters --  that were passed to the template and return a table holding the team name --  and the win-loss records (either overall, or home and away, depending on the --   input format). --  The parsers take the following parameters: --    args: table holding the parameters (indexed by numeric position) --    currentIdx: the current index from where the next set of data should be parsed --    returnData: table that the parser will update to pass additional data back to the caller. --      returnData.cIndicesRead is updated with the number of parameters that were parsed -- local readTeamInfo = { default = function(args, currentIdx, returnData) if (args[currentIdx]  == nil or            args[currentIdx+1] == nil or            args[currentIdx+2] == nil or            args[currentIdx+3] == nil or            args[currentIdx+4] == nil ) then return nil end teamInfo = { name      = mw.text.trim(args[currentIdx]), homeWins  = tonumber(mw.text.trim(args[currentIdx+1])), homeLosses = tonumber(mw.text.trim(args[currentIdx+2])), roadWins  = tonumber(mw.text.trim(args[currentIdx+3])), roadLosses = tonumber(mw.text.trim(args[currentIdx+4])), }       returnData.cIndicesRead = 5 teamInfo.wins = teamInfo.homeWins + teamInfo.roadWins teamInfo.losses = teamInfo.homeLosses + teamInfo.roadLosses return teamInfo end, -- function readTeamInfo.default

overallWinLoss = function(args, currentIdx, returnData) if (args[currentIdx]  == nil or            args[currentIdx+1] == nil or            args[currentIdx+2] == nil ) then return nil end teamInfo = { name  = mw.text.trim(args[currentIdx]), wins  = tonumber(mw.text.trim(args[currentIdx+1])), losses = tonumber(mw.text.trim(args[currentIdx+2])), }       returnData.cIndicesRead = 3 return teamInfo end, -- function readTeamInfo.default

} -- readTeamInfo object

-- -- generateTableHeader: table of functions that generate table header --  Keys are the output formats, values are functions that return a string with the table header --  The generator functions take the following parameter: --    tableHeaderInfo: table that contains the information needed for the header -- local generateTableHeader = { default = function(tableHeaderInfo) return '{| class="wikitable" style="text-align:center;"\ .. '|' .. tableHeaderInfo.division .. ']]\ ! width="51%" | Team \ ! width="6%" | W\ ! width="6%" | L\ ! width="9%" | Pct.\ ! width="8%" | GB\ ! width="10%" | Home\ ! width="10%" | Road\ '   end,  -- function generateTableHeader.default
 * + style="text-align:center;" |' .. tableHeaderInfo.navbarText .. '[[' .. tableHeaderInfo.divisionLink

winLossOnly = function(tableHeaderInfo) return '{| class="wikitable" style="text-align:center;"\ ! width="66%" | Team\ ! width="10%" | W\ ! width="10%" | L\ ! width="14%" | Pct.\ '   end,  -- function generateTableHeader.winLossOnlyNoNavBar
 * + style="text-align:center;" |' .. tableHeaderInfo.navbarText .. tableHeaderInfo.division .. '\

wildCard2012 = function(tableHeaderInfo) return '{| class="wikitable" style="text-align:center;"\ ! width="64%" | Team \ ! width="8%" | W\ ! width="8%" | L\ ! width="10%" | Pct.\ ! width="10%" | GB\ '   end,  -- function generateTableHeader.wildCard2012
 * + style="text-align:center;" |' .. tableHeaderInfo.navbarText .. 'Wild Card teams (Top 2 teams qualify for postseason) \

wildCard = function(tableHeaderInfo) local teamText = 'team' local numberOfTeamsText = 'team qualifies' if tableHeaderInfo.wildCardsPerLeague > 1 then teamText = 'teams' numberOfTeamsText = tableHeaderInfo.wildCardsPerLeague .. ' teams qualify' end return '{| class="wikitable" style="text-align:center;"\ ' for postseason) \ ! width="64%" | Team \ ! width="8%" | W\ ! width="8%" | L\ ! width="10%" | Pct.\ ! width="10%" | GB\ '   end,  -- function generateTableHeader.wildCard
 * + style="text-align:center;" |' .. tableHeaderInfo.navbarText .. 'Wild Card ' .. teamText .. ' (Top ' .. numberOfTeamsText ..

} -- generateTableHeader object

-- -- generateTeamRow: table of functions that generate a table row --  Keys are the output formats, values are functions that return a string with the table row --  The generator functions take the following parameter: --    tableRowInfo: table that contains additional the information needed for the row --    teamInfo: table that contains the team name and win-loss info -- local generateTeamRow = { default = function(teamRowInfo, teamInfo) return '|-' .. teamRowInfo.rowStyle .. '\
 * ' .. teamRowInfo.seedText .. '' .. teamInfo.name .. '\
 * ' .. teamInfo.wins .. ' || ' .. teamInfo.losses .. '\
 * ' .. teamRowInfo.winningPercentage .. '\
 * ' .. teamRowInfo.gamesBehind .. '\
 * ' .. teamInfo.homeWins .. '&zwj;–&zwj;' .. teamInfo.homeLosses ..'\
 * ' .. teamInfo.roadWins .. '&zwj;–&zwj;' .. teamInfo.roadLosses .. '\n'

end, -- function generateTeamRow.default

winLossOnly = function(teamRowInfo, teamInfo) return '|-' .. teamRowInfo.rowStyle .. '\   end,  -- function generateTeamRow.winLossOnly
 * ' .. teamRowInfo.seedText .. '' .. teamInfo.name .. '\
 * ' .. teamInfo.wins .. ' || ' .. teamInfo.losses .. '\
 * ' .. teamRowInfo.winningPercentage .. '\n'

wildCard2012 = function(teamRowInfo, teamInfo) return '|-' .. teamRowInfo.rowStyle .. '\   end,  -- function generateTeamRow.wildCard2012
 * ' .. teamRowInfo.seedText .. '' .. teamInfo.name .. '\
 * ' .. teamInfo.wins .. ' || ' .. teamInfo.losses .. '\
 * ' .. teamRowInfo.winningPercentage .. '\
 * ' .. teamRowInfo.gamesBehind .. '\n'

wildCard = function(teamRowInfo, teamInfo) return '|-' .. teamRowInfo.rowStyle .. '\   end,  -- function generateTeamRow.wildCard }  -- generateTeamRow object
 * ' .. teamRowInfo.seedText .. '' .. teamInfo.name .. '\
 * ' .. teamInfo.wins .. ' || ' .. teamInfo.losses .. '\
 * ' .. teamRowInfo.winningPercentage .. '\
 * ' .. teamRowInfo.gamesBehind .. '\n'

-- -- parseSeeds: function to parse the seeds template argument -- local function parseSeeds(seedsArg, seeds) local seedList = mw.text.split(seedsArg, '%s*,%s*') if (#seedList == 0) then return end

for idx, seed in ipairs(seedList) do       local seedData = mw.text.split(seed, '%s*:%s*') if (#seedData >= 2) then local seedNumber = tonumber(mw.text.trim(seedData[1])) local team = mw.text.trim(seedData[2]) seeds[seedNumber] = team seeds[team] = seedNumber end end end -- function parseSeeds

-- -- parseHighlightArg: function to parse the highlight template argument -- local function parseHighlightArg(highlightArg, teamsToHighlight) local teamList = mw.text.split(highlightArg, '%s*,%s*') if (#teamList == 0) then return end

for idx, team in ipairs(teamList) do       teamsToHighlight[mw.text.trim(team)] = true end

end -- function parseHighlightArg

-- -- parseTeamLInks: function to parse the team_links template argument -- local function parseTeamLinks(teamLinksArg, linkForTeam) local teamList = mw.text.split(teamLinksArg, '%s*,%s*') if (#teamList == 0) then return end

for idx, teamLinkInfo in ipairs(teamList) do       local teamData = mw.text.split(teamLinkInfo, '%s*:%s*') if (#teamData >= 2) then local team = mw.text.trim(teamData[1]) local teamLink = mw.text.trim(teamData[2]) linkForTeam[team] = teamLink end end end -- function parseTeamLinks

local function getWildCardsPerLeagueForYear(year) if year == '' then return 0 end for idx, wildCardInfo in ipairs(mlbData.wildCardInfo) do		if wildCardInfo.startYear <= year and year <= wildCardInfo.endYear then return wildCardInfo.wildCardsPerLeague; end end -- year not found, thus no wild cards for specified year return 0; end -- function getWildCardsPerLeagueForYear

-- -- function generateStandingsTable -- -- Parameters: frame object from template --  frame.args.input: input format for standings info --    if not specified, the default is team name followed by home win-loss and road win-loss records --    - overallWinLoss: team name followed by overall win-loss record -- --  frame.args.output: output format for standings table --    if not specified, the output format is based on the input format (see defaultOutputForInput table): --      - default => games behind and home and road win-loss records displayed --      - overallWinLoss => overall win-loss records displayed, no games behind column --    - winLossOnly: overall win-loss records displayed, no games behind column --    - wildCard: wildcard standings table displayed --    - wildCard2012: wildcard standings table displayed (effectively the same as wildcard; kept for backwards compatibility) -- --  frame.args.template_name: name of standings template --    if not specified, the default is  standings -- --  frame.args.seeds: list of team seedings --  frame.args.highlight: list of teams to highlight --  frame.args.team_links: list of link targets for each team --    If not specified, the default is just the team name. --    This is used to generate the season page for each team, in the form --     season -- function me.generateStandingsTable(frame) local inputFormat = 'default'

-- If the input parameter is specified in the template, use it as the input format. if (frame.args.input ~= nil) then local inputArg = mw.text.trim(frame.args.input) if (inputArg == 'overallWinLoss') then inputFormat = 'overallWinLoss' end end

local templateName = nil if (frame.args.template_name ~= nil) then templateName = frame.args.template_name end

local outputFormat = defaultOutputForInput[inputFormat] local fDisplayNavbar = true local fDisplayGamesBehind = true

-- If the output parameter is specified in the template, use it as the output format. -- Note no cross validation is performed to check if it is valid given the input format. if (frame.args.output ~= nil) then local outputArg = mw.text.trim(frame.args.output) if (outputArg == 'winLossOnly') then outputFormat = 'winLossOnly' fDisplayGamesBehind = false end if (outputArg == 'wildCard2012') then outputFormat = 'wildCard2012' end if (outputArg == 'wildCard') then outputFormat = 'wildCard' end end

local year = tonumber(mw.text.trim(frame.args.year or '0')) local division = mw.text.trim(frame.args.division or '') local divisionLink = mw.text.trim(frame.args.division_link or division) local wildCardsPerLeague = getWildCardsPerLeagueForYear(year)

local seedInfo = {} if (frame.args.seeds ~= nil) then parseSeeds(frame.args.seeds, seedInfo) end

local teamsToHighlight = {} if (frame.args.highlight ~= nil) then parseHighlightArg(frame.args.highlight, teamsToHighlight) end

local linkForTeam = {} if (frame.args.team_links ~= nil) then parseTeamLinks(frame.args.team_links, linkForTeam) end

local listOfTeams = {}; local currentArgIdx = 1;

-- Parse the unnamed parameters from the template. This consists of the -- team names and their win-loss records. while (frame.args[currentArgIdx] ~= nil) do       local returnData = { } local teamInfo = readTeamInfo[inputFormat](frame.args, currentArgIdx, returnData); if (teamInfo == nil) then break end if (linkForTeam[teamInfo.name] ~= nil) then teamInfo.teamLink = linkForTeam[teamInfo.name] else teamInfo.teamLink = teamInfo.name end table.insert(listOfTeams, teamInfo) currentArgIdx = currentArgIdx + returnData.cIndicesRead end

if (#listOfTeams == 0) then return '' end

-- table to hold list of strings that will be concatenated at the end -- to create a string with the standings table local outputBuffer = { }

local tableHeaderInfo = { division = division, divisionLink = divisionLink, wildCardsPerLeague = wildCardsPerLeague, }

if (fDisplayNavbar) then local divisionForNavbox = division if (mlbData.abbreviationForDivision[division] ~= nil) then divisionForNavbox = mlbData.abbreviationForDivision[division] end

local standingsPage if (templateName ~= nil) then standingsPage = templateName else standingsPage = year .. ' ' .. divisionForNavbox .. ' standings' end tableHeaderInfo.navbarText = Navbar.navbar({               standingsPage,                mini = 1,                style = 'float:left;width:0;',            }) end

table.insert(outputBuffer,       generateTableHeader[outputFormat](tableHeaderInfo)    )

local leadingHalfGames = nil; if (fDisplayGamesBehind) then local standingsLeaderIdx = 1 if (outputFormat == 'wildCard2012' and #listOfTeams > 1) then standingsLeaderIdx = 2 end if (outputFormat == 'wildCard' and #listOfTeams >= wildCardsPerLeague) then standingsLeaderIdx = wildCardsPerLeague end

local teamInfo = listOfTeams[standingsLeaderIdx] leadingHalfGames = (teamInfo.wins - teamInfo.losses) end

for idx, teamInfo in ipairs(listOfTeams) do   	local winningPercentage = string.format(                '%.3f', teamInfo.wins / ( teamInfo.wins + teamInfo.losses )                ) winningPercentage = string.gsub(winningPercentage, '^0', '') local teamRowInfo = { teamSeasonPage = year .. ' ' .. teamInfo.teamLink .. ' season', winningPercentage = winningPercentage, gamesBehind = '', seedText = '', rowStyle = '', }

if (fDisplayGamesBehind) then local halfGamesBehind = leadingHalfGames - (teamInfo.wins - teamInfo.losses) local prefix = nil -- if games behind is negative, take the absolute value and prefix a + -- character if (halfGamesBehind < 0) then halfGamesBehind = -halfGamesBehind prefix = '+' end if (halfGamesBehind == 0) then teamRowInfo.gamesBehind = '—' else -- if halfGamesBehind is not 0 teamRowInfo.gamesBehind = math.floor(halfGamesBehind / 2) if (halfGamesBehind % 2 == 1) then if (halfGamesBehind == 1) then teamRowInfo.gamesBehind = '½' else teamRowInfo.gamesBehind = teamRowInfo.gamesBehind .. '½'                   end end if ( prefix ~= nil ) then teamRowInfo.gamesBehind = prefix .. teamRowInfo.gamesBehind end end -- if halfGamesBehind is not 0 end -- if (fDisplayGamesBehind)

if (seedInfo[teamInfo.name] ~= nil) then teamRowInfo.seedText = '(' .. seedInfo[teamInfo.name] .. ') ' teamRowInfo.rowStyle = ' style="background:#CCFFCC"' end

if (teamsToHighlight[teamInfo.name]) then teamRowInfo.rowStyle = ' style="background:#CCFFCC"' end

table.insert(outputBuffer,           generateTeamRow[outputFormat](teamRowInfo, teamInfo)        ) end -- end of looping over listOfTeams

table.insert(outputBuffer, '|}')

return table.concat(outputBuffer)

end -- function me.generateStandingsTable

function me.generateStandingsTable_fromTemplate(frame) return me.generateStandingsTable(frame:getParent) end -- function me.generateStandingsTable_fromTemplate

return me