Jump to content

Module:Sandbox/S.A. Julio

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by S.A. Julio (talk | contribs) at 18:29, 15 November 2024 (.). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
local p = {}

-- Helper function to get display text from wikilinks
local function getDisplayText(text)
    if not text then return '' end
    
    -- First deflag if needed
    text = mw.ustring.gsub(text, '<span class="flagicon">%s*%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%][^<>]*</span>%s*', '')
    text = mw.ustring.gsub(text, '%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%]%s*', '')
    text = mw.ustring.gsub(text, '^%s*&nbsp;%s*', '')
    
    -- Then extract display text from wikilink if present
    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 all wikilinks with their display text
    text = mw.ustring.gsub(text, '%[%[[^%[%]]*%]%]', replaceLink)
    
    return text
end

-- Helper function to get short team names
local function get_short_name(s, t, n, ss, frame)
    -- If name is undefined or empty, use team code
    if not n or n == '' then
        n = t
    end
    
    -- return short name if defined
    if s and s ~= '' then
        return s
    end
    
    -- deflag if necessary
    if ss and n then
        if ss == 'noflag' then
            n = mw.ustring.gsub(n, '%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%]', '')
            n = mw.ustring.gsub(n, '^%s*&nbsp;%s*', '')
        elseif ss == 'flag' then
            n = mw.ustring.gsub(n, '(<span class="flagicon">%s*%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*link=)[^%|%[%]]*(%]%][^<>]*</span>)%s*%[%[([^%[%]%|]*)%|[^%[%]]*%]%]', '%1%3%2')
            n = mw.ustring.gsub(n, '(%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*link=)[^%|%[%]]*(%]%])%s*&nbsp;%s*%[%[([^%[%]%|]*)%|[^%[%]]*%]%]', '%1%3%2')
            n = mw.ustring.gsub(n, '(%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*link=)[^%|%[%]]*(%]%])%s*%[%[([^%[%]%|]*)%|[^%[%]]*%]%]', '%1%3%2')
            n = mw.ustring.gsub(n, '.*(<span class="flagicon">%s*%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%][^<>]*</span>).*', '%1')
            n = mw.ustring.gsub(n, '.*(%[%[[Ff][Ii][Ll][Ee]:[^%[%]]*%]%]).*', '%1')
            n = mw.ustring.gsub(n, '&nbsp;(</span>)', '%1')
        elseif ss == 'abbr' then
            -- For abbr style, we want the clean team name without any markup
            local displayName = getDisplayText(n)
            if frame then
                -- Use the Template:Abbr template
                return frame:expandTemplate{
                    title = 'abbr',
                    args = {
                        [1] = t,           -- First parameter: abbreviation
                        [2] = displayName  -- Second parameter: full name
                    }
                }
            end
        end
    end

    -- replace link text in name with team abbr if possible
    if n and t and n:match('(%[%[[^%[%]]*%]%])') and ss ~= 'abbr' then
        n = mw.ustring.gsub(n, '(%[%[[^%|%]]*%|)[^%|%]]*(%]%])', '%1' .. t .. '%2')
        n = mw.ustring.gsub(n, '(%[%[[^%|%]]*)(%]%])', '%1|' .. t .. '%2')
        n = mw.ustring.gsub(n, '(%[%[[^%|%]]*%|)([A-Z][A-Z][A-Z])(%]%])&nbsp;<span[^<>]*>%([A-Z][A-Z][A-Z]%)</span>', '%1%2%3')
        return n
    end
    -- nothing worked, so just return the unlinked team abbr
    return t or ''
end

-- Helper function to determine background colour
local function get_score_background(s, isFBR)
    local s1, s2
    -- Define the colouring based on style
    local wc = isFBR and '#BBF3FF' or '#BBF3BB'  -- blue for FBR, green otherwise
    local lc, tc = '#FBB', '#FFB' -- red and yellow remain the same
    
    -- check for override
    if s:match('^%s*<span%s+style%s*=["\'%s]*background[%-colr]*%s*:([^\'";<>]*).-$') then
        local c = mw.ustring.gsub(s,'^%s*<span%s+style%s*=["\'%s]*background[%-colr]*%s*:([^\'";<>]*).-$', '%1')
        return 'background: ' .. c ..';'
    end

    -- delink if necessary
    if s:match('^%s*%[%[[^%[%]]*%|([^%[%]]*)%]%]') then
        s = s:match('^%s*%[%[[^%[%]]*%|([^%[%]]*)%]%]')
    end
    if s:match('^%s*%[[^%[%]%s]*%s([^%[%]]*)%]') then
        s = s:match('^%s*%[[^%[%]%s]*%s([^%[%]]*)%]')
    end
    if s:match('<span[^<>]*>(.-)</span>') then
        s = s:match('<span[^<>]*>(.-)</span>')
    end

    -- get the scores
    s1 = tonumber(mw.ustring.gsub(s or '', '^%s*([%d%.]+)%s*[–%-]%s*([%d%.]+).*', '%1') or '') or ''
    s2 = tonumber(mw.ustring.gsub(s or '', '^%s*([%d%.]+)%s*[–%-]%s*([%d%.]+).*', '%2') or '') or ''

    -- return colouring if possible
    if s1 ~= '' and s2 ~= '' then
        if s1 > s2 then
            return 'background: ' .. wc .. ';'
        elseif s2 > s1 then
            return 'background: ' .. lc .. ';'
        else
            return 'background: ' .. tc .. ';'
        end
    end
    return ''
end

-- Helper function to process team order
local function getTeamList(args)
    local teams = {}
    
    -- Always check team_order first and use it if present
    if args.team_order and args.team_order:match("%S") then
        for team in args.team_order:gmatch('[^,%s]+') do
            table.insert(teams, team)
        end
        return teams
    end
    
    -- Only if team_order is nil or empty, try numbered parameters
    local i = 1
    while args['team' .. i] do
        table.insert(teams, args['team' .. i])
        i = i + 1
    end
    
    return teams
end

-- Helper function to split match list into array
local function getMatchList(matchList)
    if not matchList then return {} end
    local matches = {}
    for match in matchList:gmatch('[^,%s]+') do
        -- Special case for 'null'
        if match == 'null' then
            table.insert(matches, match)
        else
            -- Validate match format: must be CODE_[han]
            if not match:match('^[A-Z]+_[han]$') then
                error('Invalid match format: ' .. match .. '. Must be in format CODE_h, CODE_a, or CODE_n')
            end
            table.insert(matches, match)
        end
    end
    return matches
end

-- Helper function to generate match parameter key
local function getMatchKey(team1, team2, matchNum)
    return 'match_' .. team1 .. '_' .. team2 .. (matchNum > 1 and '_' .. matchNum or '')
end

-- Function to check if any neutral matches exist (add at top of module)
local function hasNeutralMatches(teams, args)
    for _, team in ipairs(teams) do
        local matchList = getMatchList(args['match_list_' .. team])
        for _, matchCode in ipairs(matchList) do
            if matchCode:match('_n$') then
                return true
            end
        end
    end
    return false
end

-- Helper function to lookup match result for neutral matches
local function getNeutralMatchResult(args, team1, team2, matchNum)
    -- Try all possible parameter combinations for neutral matches
    local baseKey1 = 'matchn_' .. team1 .. '_' .. team2
    local baseKey2 = 'matchn_' .. team2 .. '_' .. team1
    
    -- Try without suffix first
    if matchNum == 1 then
        if args[baseKey1] then
            return args[baseKey1], false  -- false means don't reverse score
        end
        if args[baseKey2] then
            return args[baseKey2], true   -- true means reverse score
        end
    end
    
    -- Try with suffix
    local suffixKey1 = baseKey1 .. '_' .. matchNum
    local suffixKey2 = baseKey2 .. '_' .. matchNum
    if args[suffixKey1] then
        return args[suffixKey1], false
    end
    if args[suffixKey2] then
        return args[suffixKey2], true
    end
    
    -- For first matches, try with _1 suffix as fallback
    if matchNum == 1 then
        local fallbackKey1 = baseKey1 .. '_1'
        local fallbackKey2 = baseKey2 .. '_1'
        if args[fallbackKey1] then
            return args[fallbackKey1], false
        end
        if args[fallbackKey2] then
            return args[fallbackKey2], true
        end
    end
    
    return nil, false
end

-- Helper function to lookup match result for regular matches
local function getRegularMatchResult(args, team1, team2, matchNum)
    -- Try all possible parameter names
    local baseKey = 'match_' .. team1 .. '_' .. team2
    
    -- Try without suffix first
    if matchNum == 1 then
        if args[baseKey] then
            return args[baseKey]
        end
    end
    
    -- Try with suffix
    local suffixKey = baseKey .. '_' .. matchNum
    if args[suffixKey] then
        return args[suffixKey]
    end
    
    -- For first matches, try with _1 suffix as fallback
    if matchNum == 1 then
        local fallbackKey = baseKey .. '_1'
        return args[fallbackKey]
    end
    
    return nil
end

-- Modify the formatScore function to take the home_first parameter
local function formatScore(score, reverseScore, home_first, isNeutral)
    if not score or score == '' or score:find('–') then return score end
    
    local score1, score2 = score:match('(%d+)[%-–](%d+)')
    if not score1 or not score2 then return score end
    
    -- If it's a neutral match or home_first is false, use the previous logic
    if isNeutral or not home_first then
        if reverseScore and score1 ~= score2 then
            return score2 .. '–' .. score1
        end
        return score1 .. '–' .. score2
    end
    
    -- For home_first=yes, just return the score as-is since it's already home-away order
    return score1 .. '–' .. score2
end

-- Modify the processMatch function to handle home_first
local function processMatch(match, currentTeam, args, matchCounts, useHomeFirst)
    -- Special handling for 'null' matches
    if match == 'null' then
        return {
            isNull = true  -- New flag to indicate null match
        }
    end

    local opponent, venue = match:match('^([A-Z]+)_([han])$')
    if not opponent or not venue then return nil end
    
    -- Skip if team is playing themselves
    if opponent == currentTeam then
        return {
            skip = true
        }
    end
    
    -- Create a directional key for counting matches that includes venue information
    local pairKey
    if venue == 'n' then
        -- For neutral venues, sort teams alphabetically for consistent counting
        pairKey = currentTeam < opponent and 
                 currentTeam .. '_' .. opponent .. '_n' or 
                 opponent .. '_' .. currentTeam .. '_n'
    else
        -- For home/away, track direction specifically
        pairKey = (venue == 'h') and 
                 currentTeam .. '_' .. opponent .. '_h' or
                 opponent .. '_' .. currentTeam .. '_a'
    end
    
    -- Initialize match count for this specific pairing+venue
    matchCounts[pairKey] = matchCounts[pairKey] or 0
    matchCounts[pairKey] = matchCounts[pairKey] + 1
    
    local matchNum = matchCounts[pairKey]
    local isHome = venue == 'h'
    local isNeutral = venue == 'n'
    
    local score, needReverseScore
    if isNeutral then
        -- For neutral matches, try both team orders
        score, needReverseScore = getNeutralMatchResult(
            args,
            currentTeam,
            opponent,
            matchNum
        )
        
        if not score then
            score, needReverseScore = getNeutralMatchResult(
                args,
                opponent,
                currentTeam,
                matchNum
            )
            if score then
                needReverseScore = true
            end
        end
    else
        -- For regular matches
        local team1 = isHome and currentTeam or opponent
        local team2 = isHome and opponent or currentTeam
        score = getRegularMatchResult(args, team1, team2, matchNum)
        -- Only reverse score if home_first is not enabled or if we're showing the away team's perspective
        needReverseScore = not isHome and not useHomeFirst
    end
    
    return {
        opponent = opponent,
        venue = venue,
        score = score,
        reverseScore = needReverseScore,
        isNeutral = isNeutral
    }
end

local function getDisplayCode(args, team)
    -- Return the override code if it exists, otherwise return the internal code
    return args['short_' .. team] or team
end

-- Main function to build the table
function p.main(frame)
    local args = require('Module:Arguments').getArgs(frame, {trim = true})
    local yesno = require('Module:Yesno')
    local useHomeFirst = yesno(args.home_first)
    
    -- Store reference to table library before we shadow it
    local tbl = table
    
    -- Initialize HTML builder
    local root = mw.html.create()
    
    -- Add table with optional font size
    local table = root:tag('table')
        :addClass('wikitable sports-res plainrowheaders')
        :css('text-align', 'center')
    
    -- Add font-size if specified, assuming percentage if no unit given
    if args.font_size then
        local fontSize = args.font_size
        -- If the font_size contains only numbers, add % sign
        if fontSize:match('^%d+$') then
            fontSize = fontSize .. '%'
        end
        table:css('font-size', fontSize)
    end

    -- Add centering styles if parameter is set
    if yesno(args.center_table) then
        table:addClass('center-table')
    end


    -- Add caption if provided
    if args.caption then
        table:tag('caption')
            :wikitext(args.caption)
    end

    -- Find maximum number of matches for any team before we need it
    local maxMatches = 0
    local teams = getTeamList(args)
    for _, team in ipairs(teams) do
        local matchList = getMatchList(args['match_list_' .. team])
        maxMatches = math.max(maxMatches, #matchList)
    end

    -- Add regular header row
    local header = table:tag('tr')
    header:tag('th'):wikitext('Team')

    -- Add matchday columns
    for i = 1, maxMatches do
        header:tag('th'):wikitext('MD' .. i)
    end

    -- Helper function to wrap text in bold if it matches showteam
    local function boldIfHighlighted(text, isHighlighted)
        if isHighlighted then
            return "'''" .. text .. "'''"
        end
        return text
    end

    -- Process each team
    for _, currentTeam in ipairs(teams) do
        local isHighlightedTeam = args.showteam and currentTeam == args.showteam
        local teamName = args['name_' .. currentTeam] or currentTeam
        
        if args.display_codes and args.display_codes:lower() == 'inline' then
            local displayCode = getDisplayCode(args, currentTeam)
            teamName = teamName .. ' <small>(' .. displayCode .. ')</small>'
        end
        
        local matchList = getMatchList(args['match_list_' .. currentTeam])
        local matchCounts = {}
        
        local oppRow = table:tag('tr')
        oppRow:tag('th')
            :attr('scope', 'row')
            :attr('rowspan', '2')
            :wikitext(boldIfHighlighted(teamName, isHighlightedTeam))
        
        local scoreRow = table:tag('tr')
        
        local useColors = args.matches_style == 'FBR' or yesno(args.matches_style)
        
        for _, matchCode in ipairs(matchList) do
            local matchData = processMatch(matchCode, currentTeam, args, matchCounts, useHomeFirst)
            
            if matchData then
                if matchData.isNull then
                    -- Handle null match - create single cell with rowspan=2 and em dash
                    oppRow:tag('td')
                        :attr('rowspan', '2')
                        :wikitext('—')  -- em dash
                elseif matchData.skip then
                    -- Add empty cells for skipped matches
                    oppRow:tag('td'):wikitext('')
                    scoreRow:tag('td'):wikitext('')
                else
                    -- Add opponent cell with modified get_short_name call
                    local oppShortName = get_short_name(
                        args['short_' .. matchData.opponent],
                        matchData.opponent,
                        args['name_' .. matchData.opponent],
                        args.display_codes == 'abbr' and 'abbr' or args.short_style,
                        frame
                    )
                    
                    local cellStyle = ''
                    local venueDisplay = ''
    
                    if yesno(args.color_venue) then
                        if matchData.venue == 'h' then
                            cellStyle = 'background: #BBB;'
                        elseif matchData.venue == 'n' then
                            cellStyle = 'background: #FEDCBA;'
                        end
                    else
                        venueDisplay = ' <small>(' .. 
                            (matchData.venue == 'n' and 'N' or 
                             (matchData.venue == 'h' and 'H' or 'A')) .. 
                            ')</small>'
                    end
    
                    oppRow:tag('td')
                        :attr('style', cellStyle)
                        :wikitext(boldIfHighlighted(oppShortName .. venueDisplay, isHighlightedTeam))
    
                    if matchData.score then
                        local formattedScore = formatScore(
                            matchData.score, 
                            matchData.reverseScore,
                            yesno(args.home_first),
                            matchData.isNeutral
                        )

                        local bgStyle = ''
                        if useColors then
                            local scoreForColor
                            if yesno(args.home_first) and not matchData.isNeutral then
                                if matchData.venue == 'h' then
                                    scoreForColor = matchData.score
                                else
                                    local score1, score2 = matchData.score:match('(%d+)[%-–](%d+)')
                                    scoreForColor = score2 .. '–' .. score1
                                end
                            else
                                if matchData.reverseScore then
                                    local score1, score2 = matchData.score:match('(%d+)[%-–](%d+)')
                                    scoreForColor = score2 .. '–' .. score1
                                else
                                    scoreForColor = matchData.score
                                end
                            end
                            bgStyle = get_score_background(scoreForColor, args.matches_style == 'FBR')
                        end

                        scoreRow:tag('td')
                            :attr('style', bgStyle)
                            :wikitext(boldIfHighlighted(formattedScore, isHighlightedTeam))
                    else
                        scoreRow:tag('td'):wikitext('')
                    end
                end
            end
        end
    end
    
    -- Get info for footer
    local update = args.update or 'unknown'
    local start_date = args.start_date or 'unknown'
    local source = args.source
    if not source then
        source = frame:expandTemplate{
            title = 'citation needed',
            args = {
                reason = 'No source parameter defined',
                date = os.date('%B %Y')
            }
        }
    end
    
    -- Add footer
    local footer = {}
    
    -- Date updating with game/match distinction
    local eventWord = yesno(args.use_tie) and 'game' or 'match'
    
    if string.lower(update) == 'complete' then
        -- Do nothing
    elseif update == '' then
        -- Empty parameter
        tbl.insert(footer, string.format('Updated to %s(s) played on unknown.', eventWord))
    elseif string.lower(update) == 'future' then
        -- Future start date
        tbl.insert(footer, string.format('First %s(s) will be played: %s.', eventWord, start_date))
    else
        tbl.insert(footer, string.format('Updated to %s(s) played on %s.', eventWord, update))
    end
    
    -- Always add source since it will either be the provided source or citation needed template
    tbl.insert(footer, 'Source: ' .. source)
            
    -- Add score note
    local showColorText = (args.matches_style == 'FBR' or yesno(args.matches_style) or yesno(args.color_venue))
    local colorWord = yesno(args.use_tie) and 'colors' or 'colours'
    local scoreText
    if useHomeFirst then
        scoreText = 'Scores are listed with the home team\'s score first'
        if hasNeutralMatches(teams, args) then
            scoreText = scoreText .. '. Neutral matches are listed from the perspective of the team shown in the "Team" column'
        end
    else
        scoreText = string.format('Scores%s are listed from the perspective of the team shown in the "Team" column',
            showColorText and ' and legend ' .. colorWord or ''
        )
    end

    tbl.insert(footer, '<br />' .. scoreText .. '.')
    
    -- Handle venue key and legend together
    local needsVenueKey = not yesno(args.color_venue)
    local needsColorLegend = args.matches_style == 'FBR' or yesno(args.matches_style)
    local hasColorVenue = yesno(args.color_venue)
    
    if needsVenueKey or needsColorLegend or hasColorVenue then
        local combinedText = '<br />'
        local parts = {}

        -- First add venue information (either as key or colors)
        if needsVenueKey then
            -- Traditional venue key with separate label
            if needsColorLegend then
                -- If we also have color legend, use "Venue key:"
                tbl.insert(parts, 'Venue key: H = home, A = away' .. 
                    (hasNeutralMatches(teams, args) and ', N = neutral' or ''))
            else
                -- Otherwise use "Legend:" for consistency with other cases
                tbl.insert(parts, 'Legend: H = home, A = away' .. 
                    (hasNeutralMatches(teams, args) and ', N = neutral' or ''))
            end
        elseif hasColorVenue then
            -- Colored venue legend
            local matchWord = yesno(args.use_tie) and 'game' or 'match'
            local greyWord = yesno(args.use_tie) and 'Gray' or 'Grey'
            tbl.insert(parts, string.format('Legend: %s = home %s; Standard background = away %s%s', 
                greyWord,
                matchWord, 
                matchWord,
                hasNeutralMatches(teams, args) and string.format('; Orange = neutral %s', matchWord) or ''
            ))
        end
        
        -- Then add match result colors if enabled
        if needsColorLegend then
            local winColor = (args.matches_style == 'FBR') and 'Blue' or 'Green'
            if needsVenueKey then
                -- Use "Color legend:" when we have separate venue key
                if yesno(args.use_tie) then
                    tbl.insert(parts, 'Legend: ' .. winColor .. ' = win; Red = loss; Yellow = tie')
                else
                    tbl.insert(parts, 'Legend: ' .. winColor .. ' = win; Yellow = draw; Red = loss')
                end
            else
                -- If we're using color_venue, continue the legend with semicolons
                if yesno(args.use_tie) then
                    tbl.insert(parts, winColor .. ' = win; Red = loss; Yellow = tie')
                else
                    tbl.insert(parts, winColor .. ' = win; Yellow = draw; Red = loss')
                end
            end
        end
        
        -- Join parts with period if we have venue key and color legend,
        -- otherwise just concatenate the single part
        combinedText = combinedText .. tbl.concat(parts, '. ') .. '.'
        tbl.insert(footer, combinedText)
    end
    
    if args.display_codes and args.display_codes:lower() == 'footer' then
        local keyParts = {}
        
        -- Handle teams
        for i, team in ipairs(teams) do
            local teamName = args['name_' .. team]
            local displayCode = getDisplayCode(args, team)
            keyParts[i] = displayCode .. ' = ' .. teamName
        end
        
        local keyText = '<br />Team key: ' .. tbl.concat(keyParts, '; ') .. '.'
        tbl.insert(footer, keyText)
    end

    -- Add rivalry note if needed
    if args.a_note then
        tbl.insert(footer, '<br />For upcoming matches, an "a" indicates there is an article about the rivalry between the two participants.')
    end
    
    -- Create templatestyles tag
    local templatestyles = frame:extensionTag{
        name = 'templatestyles',
        args = { src = 'Module:Sports results/styles.css' }
    }
    
    -- Create footer content
    local footerHtml = ''
    if #footer > 0 then
        local footerDiv = mw.html.create('div')
            :addClass('sports-results-notes')
            :wikitext(tbl.concat(footer, ' '))
            
        footerHtml = templatestyles .. tostring(footerDiv)
    end
    
    -- Create outer overflow divs
    local outerDiv = mw.html.create('div')
        :css('overflow', 'hidden')
    
    local innerDiv = outerDiv:tag('div')
        :addClass('noresize')
        :addClass('overflowbugx')
        :css('overflow', 'auto')
    
    -- Add the table to the inner div
    innerDiv:node(root)
    
    -- Return everything wrapped together
    return tostring(outerDiv) .. footerHtml
end

return p