Module:Sports series

local p = {}

-- Function to parse and expand a template with given parameters local function expandTemplate(frame, templateName, params) return frame:expandTemplate{ title = templateName, args = params } end

local function templateExists(templateName) local title = mw.title.new('Template:' .. templateName) return title and title.exists end

-- Function to process country codes and variants OR youth team flag templates and age level, dividing parameters by the "+" sign local function processIcon(iconString) if not iconString or iconString:match("^%s*$") then return nil, nil -- Return nil for both iconCode and variant if the input is empty or only whitespace elseif iconString:find('+') then local parts = mw.text.split(iconString, '+', true) local iconCode = parts[1] local variant = parts[2] return iconCode, variant else return iconString, nil -- Return the input string as iconCode if no "+" is present end end

-- Function to determine the correct ordinal suffix for a given number for the heading local function ordinal(n) local last_digit = n % 10 local last_two_digits = n % 100 if last_digit == 1 and last_two_digits ~= 11 then return n .. 'st' elseif last_digit == 2 and last_two_digits ~= 12 then return n .. 'nd' elseif last_digit == 3 and last_two_digits ~= 13 then return n .. 'rd' else return n .. 'th' end end

-- Function to clean and process the aggregate score for comparison local function cleanScore(score) -- Return an empty string if score is nil or empty to avoid errors if not score or score:match("^%s*$") then return '' end

-- Function to replace wiki links with their display text or link text local function replaceLink(match) local pipePos = match:find("|") if pipePos then return match:sub(pipePos + 1, -3) -- Return text after the '|' else return match:sub(3, -3) -- Return text without the brackets end end

-- Replace wiki links score = score:gsub("%[%[.-%]%]", replaceLink)

-- Remove MediaWiki's unique placeholder sequences for references score = score:gsub('"`UNIQ.-QINU`"', '')

-- Remove superscript tags and their contents score = score:gsub('.- ', '')

-- Convert dashes to a standard format score = score:gsub('[–—―‒−]', '-')

-- Strip all characters except numbers, dashes and parentheses return score:gsub('[^0-9%-]+', '') end

-- Function to determine the winner based on scores within parentheses (first) or regular format (second) local function determineWinner(cleanScore, matchType, team1, team2, boldWinner, colorWinner) local team1Winner, team2Winner = false, false local score1, score2 local manualBold = false local manualColor = false

-- Handling for manual bolding if team1 and type(team1) == 'string' then manualBold1 = team1:find("") and not (team1:gsub("", ""):match("^%s*$")) team1 = team1:gsub("'''", "") end if team2 and type(team2) == 'string' then manualBold2 = team2:find("") and not (team2:gsub("", ""):match("^%s*$")) team2 = team2:gsub("'''", "") end

if manualBold1 then team1Winner = true manualBold = true end if manualBold2 then team2Winner = true manualBold = true end

-- Handling for manual coloring if team1 and type(team1) == 'string' then manualColor1 = team1:find("") and not (team1:gsub("", ""):match("^%s*$")) team1 = team1:gsub("''", "") end if team2 and type(team2) == 'string' then manualColor2 = team2:find("") and not (team2:gsub("", ""):match("^%s*$")) team2 = team2:gsub("''", "") end

if manualColor1 then if not team1Winner then team1Winner = true end manualColor = true end if manualColor2 then if not team2Winner then team2Winner = true end manualColor = true end

-- Additional check for empty team names in NT matches if matchType == 'NT' and ((not team1 or team1:match("^%s*$")) or (not team2 or team2:match("^%s*$"))) then -- Skip further processing if either team name is effectively empty return team1, team2, team1Winner, team2Winner, manualBold, manualColor end

-- Regular winner determination logic if manual bolding or coloring is not conclusive if not team1Winner and not team2Winner and (boldWinner or colorWinner) then local parenthetical = cleanScore:match('%((%d+%-+%d+)%)') local outsideParenthetical = cleanScore:match('^(%d+%-+%d+)') if parenthetical then score1, score2 = parenthetical:match('(%d+)%-+(%d+)') elseif outsideParenthetical then score1, score2 = outsideParenthetical:match('(%d+)%-+(%d+)') end

if score1 and score2 then team1Winner = tonumber(score1) > tonumber(score2) team2Winner = tonumber(score1) < tonumber(score2) end end

return team1, team2, team1Winner, team2Winner, manualBold, manualColor end

-- Function to check if any parameter in a given row is non-nil and non-empty local function anyParameterPresent(startIndex, step, args) for index = startIndex, startIndex + step - 1 do       if args[index] and args[index]:match("^%s*(.-)%s*$") ~= "" then return true end end return false end

-- Main function that processes input and returns the wikitable function p.main(frame) local args = require'Module:Arguments'.getArgs(frame, {trim = true}) local root = mw.html.create local matchType = (args.type == 'WNT' or args.type == 'MNT') and 'NT' or (args.type or 'club') -- Set default match type to 'club' local isWNT = args.type == 'WNT' -- Track if WNT was set local flagTemplate, flagParam1 local noFlagIcons = false local fillBlanks = args.fill_blanks and (args.fill_blanks == 'y' or args.fill_blanks == 'yes' or args.fill_blanks == '1' or args.fill_blanks == 'true')

-- Process flag parameter to determine flag template and variant if args.flag and args.flag:find('+') then flagTemplate, flagParam1 = processIcon(args.flag) -- Process flag icons with variants else if args.flag then flagTemplate = args.flag elseif isWNT then flagTemplate = 'fbw' -- Default to  for WNT matches elseif matchType == 'NT' then flagTemplate = 'fb' -- Default to  for NT/MNT matches else flagTemplate = 'fbaicon' -- Default to  for club matches end end

if args.flag and (flagTemplate == 'n' or flagTemplate == 'no' or flagTemplate == '0' or flagTemplate == 'false' or flagTemplate == 'null' or flagTemplate == 'none' or flagTemplate == 'noflag') then noFlagIcons = true -- Hide flag icons for club matches if matchType == 'NT' then flagTemplate = isWNT and 'fbw' or 'fb' -- Set flagTemplate to "fbw"/"fb", as disabling flags is not allowed for NT            flagParam1 = false end end

-- Check if flagTemplate exists and adjust if necessary if matchType == 'NT' and (flagTemplate ~= 'fb' and flagTemplate ~= 'fbw') then if not templateExists(flagTemplate) or not templateExists(flagTemplate .. '-rt') then flagTemplate = isWNT and 'fbw' or 'fb' end elseif not noFlagIcons and flagTemplate ~= 'fbaicon' then if not templateExists(flagTemplate) then flagTemplate = 'fbaicon' end end

local legs = (args.legs == '1' or args.legs == 'n' or args.legs == 'no' or args.legs == 'false' or args.legs == 'null' or args.legs == 'none' or args.legs == 'single' or args.legs == 'one') and 0 or tonumber(args.legs) or 2 local teamWidth = (tonumber(args['team_width']) and args['team_width'] .. 'px') or '250px' local scoreWidth = (tonumber(args['score_width']) and args['score_width'] .. 'px') or '80px' local boldWinner = args.bold_winner and (args.bold_winner == 'y' or args.bold_winner == 'yes' or args.bold_winner == '1' or args.bold_winner == 'true') local colorWinner = args.color_winner and (args.color_winner == 'y' or args.color_winner == 'yes' or args.color_winner == '1' or args.color_winner == 'true')

local tableClass = 'wikitable' local tableStyle = 'text-align: center;' if args.collapsed and (args.collapsed == 'y' or args.collapsed == 'yes' or args.collapsed == '1' or args.collapsed == 'true') then tableClass = 'wikitable mw-collapsible mw-collapsed' tableStyle = 'width: 100%; text-align: center;' end if args.nowrap and (args.nowrap == 'y' or args.nowrap == 'yes' or args.nowrap == '1' or args.nowrap == 'true') then tableStyle = tableStyle .. ' white-space: nowrap;' end

-- Create the table element local table = root:tag('table') :addClass(tableClass) :cssText(tableStyle) if args.id then table:attr('id', args.id) -- Optional id parameter to allow anchor to table end

-- Add a caption to table if the "caption" parameter is passed if args.caption then table:tag('caption'):wikitext(args.caption) end

-- Count number of columns local colCount = 3 + legs

-- Add a title row above column headings if the "title" parameter is passed if args.title then local titleRow = table:tag('tr') titleRow:tag('th') :attr('colspan', colCount) :css('text-align', 'center') :wikitext(args.title) end

-- Create the header row with team and score columns local header = table:tag('tr') local defaultTeam1 = (args.h_a == 'y' or args.h_a == 'yes' or args.h_a == '1' or args.h_a == 'true') and 'Home' or 'Team 1' local defaultTeam2 = (args.h_a == 'y' or args.h_a == 'yes' or args.h_a == '1' or args.h_a == 'true') and 'Away' or 'Team 2' header:tag('th'):css('text-align', 'right'):css('width', teamWidth):wikitext(args['team1'] or defaultTeam1) header:tag('th'):css('width', scoreWidth):wikitext(args['aggregate'] or legs == 0 and 'Score' or expandTemplate(frame, 'Abbrlink', {'Agg.', 'Aggregate score'})) header:tag('th'):css('text-align', 'left'):css('width', teamWidth):wikitext(args['team2'] or defaultTeam2)

-- Add columns for each leg if applicable if legs > 0 then for leg = 1, legs do           local legHeading

-- Check if "legN" parameter is present if args['leg' .. leg] then legHeading = args['leg' .. leg] else -- Check if "leg_prefix" parameter is present if args.leg_prefix then -- Check if leg_prefix is y, yes, 1, or true if args.leg_prefix == 'y' or args.leg_prefix == 'yes' or args.leg_prefix == '1' or args.leg_prefix == 'true' then legHeading = 'Leg ' .. leg else legHeading = args.leg_prefix .. ' ' .. leg end -- Check if "leg_suffix" parameter is present and does not equal y, yes, 1, or true elseif args.leg_suffix and args.leg_suffix ~= 'y' and args.leg_suffix ~= 'yes' and args.leg_suffix ~= '1' and args.leg_suffix ~= 'true' then legHeading = ordinal(leg) .. ' ' .. args.leg_suffix else legHeading = ordinal(leg) .. ' leg' end end

header:tag('th'):css('width', scoreWidth):wikitext(legHeading) end end

local step = (matchType == 'NT' and 3 or (noFlagIcons and 3 or 5)) + legs -- Determine the step size based on the match type and presence of flag icons local i = 1 while anyParameterPresent(i, step, args) do       local rowIndex = math.floor((i - 1) / step) + 1 local headingParam = args['heading' .. rowIndex] -- Add a heading above a given row in the table if headingParam then local headingRow = table:tag('tr') headingRow:tag('td') :attr('colspan', colCount) :css('text-align', 'center') :css('background', 'whitesmoke') :wikitext(' ' .. headingParam .. ' ') end

local row = table:tag('tr') local team1, aggregateScore, team2 local team1Winner, team2Winner, manualBold, manualColor = false, false, false, false local team1Asterick, team2Asterick = false, false

-- Process rows for national team matches if matchType == 'NT' then -- Check if team parameter beings with an asterick instead of a country code, indicating a string will be displayed instead of national team flag team1 = args[i] if team1 and team1:match("^%s*%*") then team1 = team1:gsub("^%s*%*", "") team1Asterick = true else team1, team1Variant = processIcon(args[i]) end aggregateScore = args[i+1] team2 = args[i+2] if team2 and team2:match("^%s*%*") then team2 = team2:gsub("^%s*%*", "") team2Asterick = true else team2, team2Variant = processIcon(args[i+2]) end

-- Clean the aggregate score local cleanAggregate = cleanScore(aggregateScore) -- Determine the winning team on aggregate team1, team2, team1Winner, team2Winner, manualBold, manualColor = determineWinner(cleanAggregate, matchType, team1, team2, boldWinner, colorWinner) -- Add background-color for winning team if set by user local team1Style = team1Winner and ((colorWinner or manualColor) and 'background-color: #CCFFCC;' or '') .. 'text-align: right;' or 'text-align: right;' local team2Style = team2Winner and ((colorWinner or manualColor) and 'background-color: #CCFFCC;' or '') .. 'text-align: left;' or 'text-align: left;' -- Generate text to display for each team local team1Text, team2Text if flagParam1 then -- Check whether youth team flag template with age level is used team1Text = (not team1Asterick and team1 ~= "" and team1 ~= nil) and (expandTemplate(frame, flagTemplate .. '-rt', {flagParam1, team1, variant = team1Variant})) or team1 team2Text = (not team2Asterick and team2 ~= "" and team2 ~= nil) and (expandTemplate(frame, flagTemplate, {flagParam1, team2, variant = team2Variant})) or team2 else -- Use standard national team flag template without age level team1Text = (not team1Asterick and team1 ~= "" and team1 ~= nil) and (expandTemplate(frame, flagTemplate .. '-rt', {team1, variant = team1Variant})) or team1 team2Text = (not team2Asterick and team2 ~= "" and team2 ~= nil) and (expandTemplate(frame, flagTemplate, {team2, variant = team2Variant})) or team2 end -- When set by user, adds blank flags when string is used for a team instead of national team flag template if fillBlanks then if team1Asterick then team1Text = team1Text .. ' '                end if team2Asterick then team2Text = ' ' .. team2Text end end -- Create rows for aggregate score and team names, bolded if set by user row:tag('td'):cssText(team1Style):wikitext((team1Winner and (boldWinner or manualBold)) and ' ' .. team1Text .. ' ' or team1Text) row:tag('td'):css('text-align', 'center'):css('width', scoreWidth):wikitext(aggregateScore) row:tag('td'):cssText(team2Style):wikitext((team2Winner and (boldWinner or manualBold)) and ' ' .. team2Text .. ' ' or team2Text) else -- Process rows for club matches team1 = args[i] if noFlagIcons then -- Remove use of flag icons if set by user aggregateScore = args[i+1] team2 = args[i+2] else team1Icon, team1Variant = processIcon(args[i+1]) aggregateScore = args[i+2] team2 = args[i+3] team2Icon, team2Variant = processIcon(args[i+4]) end -- Clean the aggregate score local cleanAggregate = cleanScore(aggregateScore) -- Determine the winning team on aggregate team1, team2, team1Winner, team2Winner, manualBold, manualColor = determineWinner(cleanAggregate, matchType, team1, team2, boldWinner, colorWinner) -- Add background-color for winning team if set by user local team1Style = team1Winner and ((colorWinner or manualColor) and 'background-color: #CCFFCC;' or '') .. 'text-align: right;' or 'text-align: right;' local team2Style = team2Winner and ((colorWinner or manualColor) and 'background-color: #CCFFCC;' or '') .. 'text-align: left;' or 'text-align: left;' -- Generate text, and flags (if not disabled), to display for each team local team1Text = noFlagIcons and team1 or (team1Icon ~= "" and team1Icon ~= nil) and (team1 .. ' ' .. expandTemplate(frame, flagTemplate, {team1Icon, variant = team1Variant})) or team1 local team2Text = noFlagIcons and team2 or (team2Icon ~= "" and team2Icon ~= nil) and (expandTemplate(frame, flagTemplate, {team2Icon, variant = team2Variant}) .. ' ' .. team2) or team2 -- When set by user, adds blank flags when country code parameter is left blank if fillBlanks then if not noFlagIcons then if not team1Icon or team1Icon == "" then team1Text = team1Text .. ' '                    end if not team2Icon or team2Icon == "" then team2Text = ' ' .. team2Text end end end -- Create rows for aggregate score and team names, bolded if set by user row:tag('td'):cssText(team1Style):wikitext((team1Winner and (boldWinner or manualBold)) and ' ' .. team1Text .. ' ' or team1Text) row:tag('td'):css('text-align', 'center'):css('width', scoreWidth):wikitext(aggregateScore) row:tag('td'):cssText(team2Style):wikitext((team2Winner and (boldWinner or manualBold)) and ' ' .. team2Text .. ' ' or team2Text) end

-- Add columns for each leg score if applicable if legs > 0 then for leg = 1, legs do               local legIndex = i + 4 + leg + (matchType == 'NT' and -2 or (noFlagIcons and -2 or 0)) local legScore = args[legIndex] if legScore ~= "null" then row:tag('td'):css('text-align', 'center'):css('width', scoreWidth):wikitext(legScore) end end end

i = i + step end

return tostring(root) end

return p