Content deleted Content added
No edit summary Tag: Reverted |
S.A. Julio (talk | contribs) improve link rewriting |
||
(19 intermediate revisions by 4 users not shown) | |||
Line 1:
-- Module to build tables for aggregated match results in sports
-- See documentation for details
local p = {}
Line 12 ⟶ 15:
end
-- Function to process country codes and variants
local function processIcon(iconString)
if not iconString or iconString:match("^%s*$") then
Line 75 ⟶ 78:
-- Function to determine the winner based on scores within parentheses (first) or regular format (second)
local function determineWinner(cleanAggregate
local team1Winner, team2Winner = false, false
local score1, score2
Line 111 ⟶ 114:
end
if aggregate then
if
aggregate =
end
manualColorDraw =
end
Line 137 ⟶ 138:
-- Regular winner determination logic if manual bolding or coloring is not conclusive
if not team1Winner and not team2Winner and not isDraw and not skipAutoWinner and (boldWinner or colorWinner or isFBRStyle) then
local parenthetical = cleanAggregate:match('%((%d+%-+%d+)%)')
local outsideParenthetical = cleanAggregate:match('^(%d+%-+%d+)')
Line 149 ⟶ 150:
score1 = tonumber(score1)
score2 = tonumber(score2)
if score1 > score2 then
team1Winner = true
Line 163 ⟶ 164:
if team1AwayGoals and team2AwayGoals then
team1AwayGoals, team2AwayGoals = tonumber(team1AwayGoals), tonumber(team2AwayGoals)
if team1AwayGoals > team2AwayGoals then
team1Winner = true
Line 171 ⟶ 172:
end
end
if (colorWinner or isFBRStyle) and legs == 0 then
isDraw = not team1Winner and not team2Winner
Line 179 ⟶ 180:
return team1, team2, team1Winner, team2Winner, manualBold, manualColor, isDraw, aggregate
end
-- Function to process score bold/italic formatting
function processScore(s)
if not s or s == "" then
return "", false
end
local scoreFormat = false
-- Check for 5+ apostrophes (both bold and italic)
if s:match("'''''+") then
scoreFormat = "both"
s = s:gsub("''+", "")
return s, scoreFormat
end
-- Check for 3+ apostrophes (bold)
if s:match("'''+") then
scoreFormat = "bold"
s = s:gsub("''+", "")
return s, scoreFormat
end
-- Check for 2 apostrophes (italic)
if s:match("''") then
scoreFormat = "italic"
s = s:gsub("''+", "")
return s, scoreFormat
end
-- If no matches found, return original string and false
return s, scoreFormat
end
-- Function to check if any parameter in a given row is non-nil and non-empty
local function anyParameterPresent(startIndex, step, args)
-- Check regular parameters
for index = startIndex, startIndex + step - 1 do
if args[index] and args[index]:match("^%s*(.-)%s*$") ~= "" then
Line 188 ⟶ 223:
end
end
-- Check aggregate note
local rowIndex = math.floor((startIndex - 1) / step) + 1
local aggNote = args['note_agg_' .. rowIndex]
if aggNote and aggNote:match("^%s*(.-)%s*$") ~= "" then
end
-- Check leg notes
local numLegs = step - (noFlagIcons and 3 or 5) -- Calculate number of legs
for leg = 1, numLegs do
local legNote = args['note_leg' .. leg .. '_' .. rowIndex]
if legNote and legNote:match("^%s*(.-)%s*$") ~= "" then
return true
end
end
return false
end
Line 213 ⟶ 257:
-- Check for walkover-related strings (never shown in small text)
if str:lower():match("walkover") or str:lower():match("w%.o%.") or str:lower():match("w/o") or str:lower():match("bye") then
return false
end
Line 233 ⟶ 277:
-- Remove opening and closing HTML tags
str = str:gsub("</?%w+[^>]*>", "")
-- Remove apostrophes
str = str:gsub("''+", "")
-- Remove all whitespace
Line 245 ⟶ 292:
end
-- Function to
local function
if not
return str
end
-- Add the base link to anchor links when the module is transcluded on another page
if baselink ~= '' then
str = mw.ustring.gsub(str, '(%[%[)(#[^%[%]]*%|)', '%1' .. baselink .. '%2')
end
-- Remove redundant page references when viewing anchors on the current page
if currentPageTitle and currentPageTitle ~= "" then
local escapedTitle = currentPageTitle:gsub("([%(%)%.%%%+%-%*%?%[%^%$])", "%%%1")
local titlePattern = '%[%[' .. escapedTitle .. '(#[^%[%]]*%|)'
str = mw.ustring.gsub(str, titlePattern, '[[%1')
end
return str
end
-- Function to format the dashes and winning notes for aggregate/leg score parameters, and divide the score from references/notes/superscripts
local function format_and_extract_score(s, addSpan)
if not s then return '', '' end -- Return empty strings if input is nil
-- Handle walkovers
if s:match("^%s*[Ww]%s*[/.]%s*[Oo]%s*%.?%s*$") then
return "[[Walkover|w/o]]", ""
end
local function format_dash(pattern)
Line 262 ⟶ 335:
format_dash('%s*(%[[^%[%]%s]*%s+[%d%.]+)%s*&[MmNn][Dd][Aa][Ss][Hh];%s*([%d%.]+)')
--
local supStart = s:find('<sup')
local placeholderStart = s:find('\127%\'"`UNIQ')
-- Function to find the first parenthesis outside of wikilinks
local function find_paren_outside_wikilinks(s)
local pos = 1
while true do
pos = s:find('%(', pos)
if not pos then break end
local beforeParen = s:sub(1, pos - 1)
local openLinks = select(2, beforeParen:gsub('%[%[', '')) - select(2, beforeParen:gsub('%]%]', ''))
if openLinks == 0 then
return pos
end
pos = pos + 1
end
return nil
end
local parenStart = find_paren_outside_wikilinks(s)
local startPositions = {}
if supStart then table.insert(startPositions, supStart) end
if placeholderStart then table.insert(startPositions, placeholderStart) end
if parenStart then table.insert(startPositions, parenStart) end
local scoreMatch, endText
if #startPositions > 0 then
local startPos = math.min(unpack(startPositions))
-- Find the last non-whitespace character before startPos
local scoreEnd = s:sub(1, startPos - 1):match(".*%S") or ""
scoreEnd = #scoreEnd
-- Extract the score and endText
scoreMatch = s:sub(1, scoreEnd)
endText = s:sub(scoreEnd + 1)
else
-- If no match found, return the entire score
scoreMatch = s
endText = ""
end
-- Format winning notes in brackets (only if endText is not empty)
if endText ~= "" then
if addSpan then
endText = mw.ustring.gsub(endText, '(%(%d+%s*–%s*%d+)%s*[Pp]%.?[EeSs]?%.?[NnOo]?%.?%)', '<span class="nowrap">%1 [[Penalty shoot-out (association football)|p]])</span>')
endText = mw.ustring.gsub(endText, '%([Aa]%.?[Ee]%.?[Tt]%.?%)', '<span class="nowrap">([[Overtime (sports)#Association football|a.e.t.]])</span>')
else
endText = mw.ustring.gsub(endText, '(%(%d+%s*–%s*%d+)%s*[Pp]%.?[EeSs]?%.?[NnOo]?%.?%)', '%1 [[Penalty shoot-out (association football)|p]])')
endText = mw.ustring.gsub(endText, '%([Aa]%.?[Ee]%.?[Tt]%.?%)', '([[Overtime (sports)#Association football|a.e.t.]])')
end
endText = mw.ustring.gsub(endText, '%([Aa]%.?[Gg]?%.?[Rr]?%.?%)', '([[Away goals rule|a]])')
end
return scoreMatch, endText
end
-- Function to
local function
local function cleanTeam(str, defaultName)
str = str:gsub('<sup.->.-</sup>', '')
str = str:gsub("</?%w+[^>]*>", "")
str = str:gsub('\127%\'"`UNIQ.-QINU`"%\'\127', '')
str = str:gsub("%[%[[Ff]ile:[^%]]+%]%]", "")
str = str:gsub("%[%[[Ii]mage:[^%]]+%]%]", "")
str = str:gsub("%[%[.-%]%]", replaceLink)
str = str:gsub("%s* %s*", "")
str = str:match("^%s*(.-)%s*$") -- Remove leading and trailing whitespace
return str ~= "" and str or defaultName
end
return defaultName
end
team1 = cleanTeam(team1, "Team 1")
team2 = cleanTeam(team2, "Team 2")
if score and score:match("%S") then
local linkScore = score
if score:find('%[') then
linkScore = score:match('^([%d%.]+–[%d%.]+)')
if not linkScore then
return score
end
end
if linkScore then
local link
if isSecondLeg then
link = "[[#" .. team2 .. " v " .. team1 .. "|" .. linkScore .. "]]"
else
link = "[[#" .. team1 .. " v " .. team2 .. "|" .. linkScore .. "]]"
end
return link .. score:sub(#linkScore + 1)
end
end
return score
end
-- Function to process notes for aggregate and leg scores
local function processNote(frame, notes, noteKey, noteText, endText, rowIndex, rand_val, noteGroup, baselink, currentPageTitle)
if not noteText then return endText, notes end
if noteText:match("^%s*<sup") or noteText:match("^\127%\'%\"`UNIQ") then
return noteText .. endText, notes
end
local function createInlineNote(name)
return frame:extensionTag{
name = 'ref',
args = {
name = name,
group = noteGroup
}
}
end
-- Check if noteText is a reference to another note
local referenced_note = noteText:match("^(agg_%d+)$") or noteText:match("^(leg%d+_%d+)$")
if referenced_note then
local referenced_note_id = '"table_note_' .. referenced_note .. '_' .. rand_val .. '"'
return endText .. createInlineNote(referenced_note_id), notes
end
-- Process anchor links in noteText before storing
if noteText:find("%[%[") then
noteText = rewriteAnchorLinks(noteText, baselink, currentPageTitle)
end
local note_id = '"table_note_' .. noteKey .. '_' .. rowIndex .. '_' .. rand_val .. '"'
if not notes[note_id] then
notes[note_id] = noteText
end
return endText .. createInlineNote(note_id), notes
end
-- Function to generate the footer if necessary
local function createFooter(frame, notes, noteGroup, isFBRStyle, displayNotes, externalNotes, legs)
local needFooter = (isFBRStyle and legs == 0) or displayNotes or (next(notes) ~= nil)
if not needFooter then
return '' -- Return an empty string if no footer is needed
end
local divContent = mw.html.create('div')
:addClass('sports-series-notes')
if isFBRStyle and legs == 0 then
divContent:wikitext("Legend: Blue = home team win; Yellow = draw; Red = away team win.")
end
if (next(notes) ~= nil and not externalNotes) or displayNotes then
divContent:wikitext((isFBRStyle and legs == 0) and "<br>Notes:" or "Notes:")
end
local footer = tostring(divContent)
if next(notes) ~= nil or displayNotes then
local noteDefinitions = {}
for noteId, noteText in pairs(notes) do
if type(noteId) == 'string' and noteId:match('^"table_note') then
table.insert(noteDefinitions, frame:extensionTag{
name = 'ref',
args = {
name = noteId,
group = noteGroup
},
content = noteText
})
end
end
if externalNotes then
local hiddenRefs = mw.html.create('span')
:addClass('sports-series-hidden')
:wikitext(table.concat(noteDefinitions))
if isFBRStyle and legs == 0 then
footer = footer .. tostring(hiddenRefs)
else
footer = tostring(hiddenRefs)
end
else
local reflistArgs = {
refs = table.concat(noteDefinitions),
group = noteGroup
}
footer = footer .. frame:expandTemplate{
title = 'reflist',
args = reflistArgs
}
end
end
return footer
end
-- Main function that processes input and returns the wikitable
function p.main(frame)
local args = require('
local yesno = require('Module:Yesno')
-- Check for section transclusion
Line 297 ⟶ 553:
local root = mw.html.create()
local templatestyles = frame:extensionTag{
name = 'templatestyles',
args = { src = 'Screen reader-only/styles.css' }
} .. frame:extensionTag{
name = 'templatestyles',
args = { src = 'Module:Sports series/styles.css' }
}
root:wikitext(templatestyles)
local flagYesno = yesno(args.flag)
local showFlags = flagYesno ~= false
local noFlagIcons = not showFlags
local fillBlanks = yesno(args.fill_blanks)
local generateLinks = yesno(args.generate_links)
local solidCell = yesno(args.solid_cell) or args.solid_cell == 'grey' or args.solid_cell == 'gray'
local baselink = frame:getParent():getTitle()
if currentPageTitle == baselink then baselink = '' end
local notes = {}
local noteGroup = args.note_group or 'lower-alpha'
local noteListValue = yesno(args.note_list)
local displayNotes = noteListValue == true
local externalNotes = noteListValue == false
math.randomseed(os.clock() * 10^8) -- Initialize random number generator
local rand_val = math.random()
-- Process the font size parameter
Line 314 ⟶ 587:
fontSize = math.max(fontSize, 85) -- Ensure font size is at least 85
end
end
-- Process flag parameter to determine flag template and variant
local flagTemplate = 'fbaicon'
local flagSize = args.flag_size
if showFlags then
if args.flag and args.flag ~= '' and not flagYesno then
flagTemplate = args.flag:gsub('^Template:', '')
end
if flagSize and not flagSize:match('px$') then
end
end
-- Determine whether line should be displayed
local showCountry = args.show_country
local function shouldShowRow(team1Icon, team2Icon)
if not showCountry or noFlagIcons then
end
return team1Icon == showCountry or team2Icon == showCountry
end
local legs = 2
if args.legs
if yesno(args.legs) == false or args.legs == '1'
legs = 0
else
legs = tonumber(args.legs) and math.max(tonumber(args.legs), 2) or 2
end
end
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 =
local colorWinner =
local matchesStyle = args.matches_style
local isFBRStyle = matchesStyle and matchesStyle:upper() == "FBR"
local isHA = yesno(args.h_a
local disableAwayGoals = yesno(args.away_goals) ==
local disableSmallText = yesno(args.small_text) ==
local noWrapValue = yesno(args.nowrap)
local noWrap = noWrapValue == true
local disableNoWrap = noWrapValue == false
local aggFormat
local tableClass = 'wikitable sports-series'
local
if doCollapsed then
tableClass = tableClass .. '
end
if
end
if fontSize then
end
Line 392 ⟶ 655:
table:attr('id', args.id) -- Optional id parameter to allow anchor to table
end
if noWrap then
table:attr('data-nowrap', 'y')
end
Line 409 ⟶ 671:
-- Add a title row above column headings if the "title" parameter is passed
if args.title then
local titleRow = table:tag('tr'):addClass('title-row')
titleRow:tag('th')
:attr('colspan', colCount)
Line 418 ⟶ 680:
-- Create the header row with team and score columns
local header = table:tag('tr')
local defaultTeam1 = isHA and 'Home team' or 'Team 1'
local defaultTeam2 = isHA and 'Away team' or 'Team 2'
header:tag('th'):attr('scope', 'col'):css('width', teamWidth):wikitext(args['team1'] or defaultTeam1)
header:tag('th'):attr('scope', 'col'):css('width', scoreWidth):wikitext(args['aggregate'] or legs == 0 and 'Score' or '[[Aggregate score|<abbr title="Aggregate score">Agg.</abbr>]]<span class="sr-only"> Tooltip Aggregate score</span>')
header:tag('th'):attr('scope', 'col'):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 = args['leg' .. leg]
-- Check if "legN" parameter is present
if
if args.leg_prefix then
legHeading = ordinal(leg) .. ' ' .. args.leg_suffix
else
Line 460 ⟶ 702:
end
header:tag('th'):attr('scope', 'col'):css('width', scoreWidth):wikitext(legHeading)
end
end
local step =
local i = 1
while anyParameterPresent(i, step, args) do
local rowIndex = math.floor((i - 1) / step) + 1
local aggNote = args['note_agg_' .. rowIndex]
local headingParam = args['heading' .. rowIndex]
local team1, team2, aggregateScore, aggregateEndText, legEndText, team1Icon, team2Icon, team1Variant, team2Variant
local team1Winner, team2Winner, manualBold, manualColor, isDraw = false, false, false, false, false
local leg1Score, leg2Score = false, false
-- Process rows
if noFlagIcons
aggregateScore = args[i+1]
team2 = args[i+2]
else
team2Icon,
end
--
if
if headingParam and not
end
--
if legs == 2 then
if noFlagIcons then
leg1Score = args[i+3]
leg2Score = args[i+4]
else
leg1Score = args[i+5]
leg2Score = args[i+6]
end
end
local
aggregateScore, aggFormat = processScore(aggregateScore)
-- Format anchor links for aggregate score
local aggParen = cleanAggregate:match("%(.*%(")
local aggSpan = (disableNoWrap or (not noWrap and not disableNoWrap and aggParen))
aggregateScore, aggregateEndText = format_and_extract_score(aggregateScore, aggSpan)
-- Apply link rewriting to note text before creating the note
aggregateEndText, notes = processNote(frame, notes, 'agg', aggNote, aggregateEndText, rowIndex, rand_val, noteGroup, baselink, currentPageTitle)
if generateLinks and legs == 0 then
-- Skip link generation for "Bye" entries
local isBye = aggregateScore:match("^%s*[Bb][Yy][Ee]%s*$") or aggregateScore:match("|[Bb][Yy][Ee]%]%]")
if not isBye then
aggregateScore = cleanAndGenerateLinks(team1, team2, aggregateScore, false)
end
end
local skipAutoWinner = legs == 0 and aggregateScore ~= '' and checkSmallText(aggregateScore)
-- Determine the winning team on aggregate
team1, team2, team1Winner, team2Winner, manualBold, manualColor, isDraw, aggregateScore = determineWinner(cleanAggregate, team1, team2, boldWinner, colorWinner, aggregateScore, isFBRStyle, legs, leg1Score, leg2Score, disableAwayGoals, skipAutoWinner, aggFormat)
-- Function to create flag template parameters
local function getFlagParams(icon, variant)
local params = {icon, variant = variant}
if flagSize then
params.size = flagSize
end
return params
end
local team1Text = noFlagIcons and (team1 or '') or ((team1Icon ~= "" and team1Icon ~= nil) and ((team1 or '') .. ' ' .. expandTemplate(frame, flagTemplate, getFlagParams(team1Icon, team1Variant))) or (team1 or ''))
local team2Text = noFlagIcons and (team2 or '') or ((team2Icon ~= "" and team2Icon ~= nil) and (expandTemplate(frame, flagTemplate, getFlagParams(team2Icon, team2Variant)) .. ' ' .. (team2 or '')) or (team2 or ''))
-- When set by user, adds blank flag placeholder next to team names
if fillBlanks and showFlags
local
if not team1Icon or team1Icon == "" then
team1Text = team1Text .. ' '
end
if not team2Icon or team2Icon == "" then
team2Text =
end
end
if not disableSmallText and skipAutoWinner then
else
aggregateContent = aggregateScore .. aggregateEndText
-- Create aggregate score cell with conditional styling
if isFBRStyle and legs == 0 then
if team1Winner then
aggregateClass = 'fbr-home-win'
elseif team2Winner then
aggregateClass = 'fbr-away-win'
elseif isDraw then
aggregateClass = 'draw'
end
elseif isDraw then
end
if not disableNoWrap and (not noWrap and aggParen) then
aggregateClass = (aggregateClass ~= '' and aggregateClass .. ' ' or '') .. 'allow-wrap'
end
-- Create rows for aggregate score and team names, bolded if set by user
row:tag('td'):
row:tag('td'):
row:tag('td'):
-- Add columns for each leg score if applicable
if legs > 0 then
for leg = 1, legs do
local legIndex = i + 4 + leg +
local legScore = args[legIndex]
if legScore
if
legScore = '—'
end
end
local
-- Apply link rewriting to note text before creating the note
legEndText, notes = processNote(frame, notes, 'leg' .. leg, legNote, legEndText, rowIndex, rand_val, noteGroup, baselink, currentPageTitle)
if generateLinks and not aggregateContent:lower():find("bye") then
if leg == 1 then
legScore = cleanAndGenerateLinks(team1, team2, legScore, false)
elseif leg == 2 then
legScore = cleanAndGenerateLinks(team1, team2, legScore, true)
end
end
if legFormat == 'bold' or legFormat == 'both' then legScore = '<b>' .. legScore .. '</b>' end
if legFormat == 'italic' or legFormat == 'both' then legScore = '<i>' .. legScore .. '</i>' end
local legContent
if not disableSmallText and legScore ~= '' and checkSmallText(legScore) then
legContent = '<span class="sports-series-small">' .. legScore .. '</span>' .. legEndText
else
legContent = legScore .. legEndText
end
local legClass = ''
if not disableNoWrap and (not noWrap and legParen) then
legClass = 'allow-wrap'
end
-- Write cells for legs
row:tag('td'):addClass(legClass ~= '' and legClass or nil):wikitext(legContent)
end
end
end
Line 637 ⟶ 883:
end
-- Generate footer text
local footerText = createFooter(frame, notes, noteGroup, isFBRStyle, displayNotes, externalNotes, legs)
root:wikitext(footerText)
local tableCode = tostring(root)
-- Rewrite anchor links for the entire table (except for notes which were handled separately)
tableCode = rewriteAnchorLinks(tableCode, baselink, currentPageTitle)
-- Return the completed table with rewritten links
return tableCode
end
|