Module:Football group summary
local p = {}
local MONTHS = {
january = 1, february = 2, march = 3, april = 4,
may = 5, june = 6, july = 7, august = 8,
september = 9, october = 10, november = 11, december = 12,
}
local MONTH_NAMES = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December",
}
local function trim(s)
if not s then return "" end
return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end
local function normalizeSpaces(s)
if not s then return "" end
s = s:gsub(" ", " ")
s = s:gsub("\194\160", " ")
s = s:gsub("&", "&")
return s
end
local function findTemplateEnd(s, openStart)
local i = openStart + 2
local depth = 1
local len = #s
while i <= len do
local c2 = s:sub(i, i + 1)
if c2 == "{{" then
depth = depth + 1
i = i + 2
elseif c2 == "}}" then
depth = depth - 1
if depth == 0 then
return i + 1
end
i = i + 2
else
i = i + 1
end
end
return nil
end
local function splitTopLevelPipes(body)
local parts = {}
local buf = {}
local i = 1
local len = #body
local braceDepth = 0
local bracketDepth = 0
local tagDepth = 0
while i <= len do
local c = body:sub(i, i)
local c2 = body:sub(i, i + 1)
if c2 == "{{" then
braceDepth = braceDepth + 1
buf[#buf + 1] = c2
i = i + 2
elseif c2 == "}}" then
braceDepth = math.max(0, braceDepth - 1)
buf[#buf + 1] = c2
i = i + 2
elseif c2 == "[[" then
bracketDepth = bracketDepth + 1
buf[#buf + 1] = c2
i = i + 2
elseif c2 == "]]" then
bracketDepth = math.max(0, bracketDepth - 1)
buf[#buf + 1] = c2
i = i + 2
elseif c == "<" then
tagDepth = tagDepth + 1
buf[#buf + 1] = c
i = i + 1
elseif c == ">" then
tagDepth = math.max(0, tagDepth - 1)
buf[#buf + 1] = c
i = i + 1
elseif c == "|" and braceDepth == 0 and bracketDepth == 0 and tagDepth == 0 then
parts[#parts + 1] = table.concat(buf)
buf = {}
i = i + 1
else
buf[#buf + 1] = c
i = i + 1
end
end
parts[#parts + 1] = table.concat(buf)
return parts
end
local function parseTemplateBody(body)
local segments = splitTopLevelPipes(body)
local result = { _name = trim(segments[1] or "") }
local positional = 0
for i = 2, #segments do
local seg = segments[i]
local eqPos
local depth, brack = 0, 0
for j = 1, #seg do
local c = seg:sub(j, j)
local c2 = seg:sub(j, j + 1)
if c2 == "{{" then depth = depth + 1
elseif c2 == "}}" then depth = depth - 1
elseif c2 == "[[" then brack = brack + 1
elseif c2 == "]]" then brack = brack - 1
elseif c == "=" and depth == 0 and brack == 0 then
eqPos = j
break
end
end
if eqPos then
local name = trim(seg:sub(1, eqPos - 1))
local value = trim(seg:sub(eqPos + 1))
result[name] = value
else
positional = positional + 1
result[positional] = trim(seg)
end
end
return result
end
local function stripQuotes(s)
if not s then return s end
s = trim(s)
local q = s:sub(1, 1)
if (q == '"' or q == "'") and s:sub(-1) == q then
return s:sub(2, -2)
end
return s
end
local function extractLabeledSection(wikitext, sectionName)
local target = trim(sectionName)
local out = {}
local i = 1
local len = #wikitext
while i <= len do
local tagStart, tagEnd, attrs =
wikitext:find("<%s*[Ss][Ee][Cc][Tt][Ii][Oo][Nn]%s+([^<>]-)/?%s*>", i)
if not tagStart then break end
local kind = attrs:match("^%s*([Bb][Ee][Gg][Ii][Nn])%s*=") and "begin"
or attrs:match("^%s*([Ee][Nn][Dd])%s*=") and "end"
local nameMatch
if kind then
nameMatch = attrs:match("=%s*(.+)%s*$")
if nameMatch then nameMatch = stripQuotes(nameMatch) end
end
if kind == "begin" and nameMatch == target then
local searchFrom = tagEnd + 1
while true do
local eStart, eEnd, eAttrs =
wikitext:find("<%s*[Ss][Ee][Cc][Tt][Ii][Oo][Nn]%s+([^<>]-)/?%s*>", searchFrom)
if not eStart then break end
local eKind = eAttrs:match("^%s*([Ee][Nn][Dd])%s*=") and "end"
or eAttrs:match("^%s*([Bb][Ee][Gg][Ii][Nn])%s*=") and "begin"
local eName = eAttrs:match("=%s*(.+)%s*$")
if eName then eName = stripQuotes(eName) end
if eKind == "end" and eName == target then
out[#out + 1] = wikitext:sub(tagEnd + 1, eStart - 1)
i = eEnd + 1
break
end
searchFrom = eEnd + 1
end
if i <= tagEnd then i = tagEnd + 1 end
else
i = tagEnd + 1
end
end
return table.concat(out, "\n")
end
local function extractHeaderSection(wikitext, headerName)
local target = trim(headerName)
local lines = {}
local lineStart = 1
for s in wikitext:gmatch("[^\n]*\n?") do
if #s == 0 then break end
lines[#lines + 1] = { text = s, start = lineStart }
lineStart = lineStart + #s
end
local found
local foundLevel
for idx, line in ipairs(lines) do
local eqL, name, eqR = line.text:match("^%s*(=+)%s*(.-)%s*(=+)%s*$")
if eqL and name and eqR then
local level = math.min(#eqL, #eqR)
if trim(name) == target then
found = idx
foundLevel = level
break
end
end
end
if not found then return "" end
local startIdx = found + 1
local endIdx = #lines
for idx = startIdx, #lines do
local line = lines[idx]
local eqL, _, eqR = line.text:match("^%s*(=+)%s*(.-)%s*(=+)%s*$")
if eqL and eqR then
local level = math.min(#eqL, #eqR)
if level <= foundLevel then
endIdx = idx - 1
break
end
end
end
local out = {}
for idx = startIdx, endIdx do
out[#out + 1] = lines[idx].text
end
return table.concat(out)
end
local function fetchArticle(titleStr)
if not titleStr or trim(titleStr) == "" then return "" end
local title = mw.title.new(trim(titleStr))
if not title or not title.exists then return "" end
return title:getContent() or ""
end
local function identifyTransclusion(wikitext, openStart)
local closeEnd = findTemplateEnd(wikitext, openStart)
if not closeEnd then return nil end
local body = wikitext:sub(openStart + 2, closeEnd - 2)
local segments = splitTopLevelPipes(body)
local head = trim(segments[1] or "")
local fullTitle = head:match("^:%s*(.+)$")
if fullTitle then
return "full", trim(fullTitle), nil, closeEnd
end
local fn, target = head:match("^#%s*([%w%-]+)%s*:%s*(.+)$")
if fn and target then
local fnLower = fn:lower()
local arg = trim(segments[2] or "")
if fnLower == "lst" or fnLower == "section" then
return "lst", trim(target), arg, closeEnd
elseif fnLower == "lsth" or fnLower == "section-h" then
return "lsth", trim(target), arg, closeEnd
end
end
return nil
end
local function findFootballBoxInvocations(wikitext, fetchFn)
fetchFn = fetchFn or fetchArticle
local results = {}
local i = 1
local len = #wikitext
while i <= len do
local openStart = wikitext:find("{{", i, true)
if not openStart then break end
local after = wikitext:sub(openStart + 2, openStart + 80):lower()
local stripped = after:gsub("^%s+", "")
local isFootballBox = false
if stripped:match("^football%s*box[%s|}]") then
isFootballBox = true
elseif stripped:match("^#invoke:%s*football%s*box%s*|%s*main[%s|}]") then
isFootballBox = true
end
if isFootballBox then
local closeEnd = findTemplateEnd(wikitext, openStart)
if closeEnd then
local body = wikitext:sub(openStart + 2, closeEnd - 2)
results[#results + 1] = body
i = closeEnd + 1
else
i = openStart + 2
end
else
local kind, target, arg, closeEnd =
identifyTransclusion(wikitext, openStart)
if kind and target then
local fetched = fetchFn(target)
local slice = ""
if fetched and fetched ~= "" then
if kind == "full" then
slice = fetched
elseif kind == "lst" then
slice = extractLabeledSection(fetched, arg or "")
elseif kind == "lsth" then
slice = extractHeaderSection(fetched, arg or "")
end
end
if slice and slice ~= "" then
local nestedBoxes = findFootballBoxInvocations(slice,
function() return "" end)
for _, b in ipairs(nestedBoxes) do
results[#results + 1] = b
end
end
i = (closeEnd or (openStart + 1)) + 1
else
i = openStart + 2
end
end
end
return results
end
local function findNestedTemplate(s, targetName)
local normalizedTarget = targetName:lower():gsub("%s+", " ")
local i = 1
local len = #s
while i <= len do
local openStart = s:find("{{", i, true)
if not openStart then return nil end
local closeEnd = findTemplateEnd(s, openStart)
if not closeEnd then return nil end
local body = s:sub(openStart + 2, closeEnd - 2)
local firstPipe = body:find("|", 1, true)
local name
if firstPipe then
name = trim(body:sub(1, firstPipe - 1))
else
name = trim(body)
end
local normalizedName = name:lower():gsub("%s+", " ")
if normalizedName == normalizedTarget then
return openStart, closeEnd, parseTemplateBody(body)
end
i = openStart + 2
end
return nil
end
local function formatDate(d, style)
if not d then return "" end
if style == "mdy" then
return string.format("%s %d, %d", MONTH_NAMES[d.month], d.day, d.year)
else
return string.format("%d %s %d", d.day, MONTH_NAMES[d.month], d.year)
end
end
local function parseDateParam(value)
if not value or value == "" then return nil, "" end
value = trim(value)
local sdOpen = value:match("^{{%s*[Ss][Tt][Aa][Rr][Tt]%s+[Dd][Aa][Tt][Ee]%s*[|}]")
if sdOpen then
local closeEnd = findTemplateEnd(value, 1)
if closeEnd then
local body = value:sub(3, closeEnd - 2)
local params = parseTemplateBody(body)
local y = tonumber(params[1])
local m = tonumber(params[2])
local d = tonumber(params[3])
if y and m and d and m >= 1 and m <= 12 then
local style
if params["df"] and trim(params["df"]):lower() == "y" then
style = "dmy"
elseif params["mf"] and trim(params["mf"]):lower() == "y" then
style = "mdy"
else
style = "mdy"
end
local dt = { year = y, month = m, day = d }
return dt, formatDate(dt, style)
end
end
end
local plain = normalizeSpaces(value)
plain = plain:gsub("%[%[[^%[%]|]-|([^%[%]]-)%]%]", "%1")
plain = plain:gsub("%[%[([^%[%]]-)%]%]", "%1")
plain = plain:gsub("<[^<>]->", "")
plain = trim(plain:gsub("%s+", " "))
local y, m, d = plain:match("(%d%d%d%d)%-(%d%d)%-(%d%d)")
if y then
local dt = { year = tonumber(y), month = tonumber(m), day = tonumber(d) }
return dt, formatDate(dt, "mdy")
end
local day, mname, year = plain:match("(%d%d?)%s+([A-Za-z]+)%s+(%d%d%d%d)")
if day and mname and year then
local mi = MONTHS[mname:lower()]
if mi then
local dt = { year = tonumber(year), month = mi, day = tonumber(day) }
return dt, plain
end
end
local mname2, day2, year2 = plain:match("([A-Za-z]+)%s+(%d%d?),?%s+(%d%d%d%d)")
if mname2 and day2 and year2 then
local mi = MONTHS[mname2:lower()]
if mi then
local dt = { year = tonumber(year2), month = mi, day = tonumber(day2) }
return dt, plain
end
end
return nil, plain
end
local function buildScoreOutput(scoreValue, aetValue)
scoreValue = scoreValue or ""
local output
local startPos, endPos, params = findNestedTemplate(scoreValue, "score link")
if startPos and params then
local p1 = params[1] or ""
local p2 = params[2] or ""
local linked = "[[" .. p1 .. "|" .. p2 .. "]]"
local before = scoreValue:sub(1, startPos - 1)
local after = scoreValue:sub(endPos + 1)
output = before .. linked .. after
else
output = scoreValue
end
output = trim(output)
if aetValue and trim(aetValue) ~= "" then
output = output .. " ([[Overtime (sports)#Association football|a.e.t.]])"
end
return output
end
local function buildMatchRecord(params, sourceOrder)
local record = { sourceOrder = sourceOrder }
local dt, dateStr = parseDateParam(params["date"])
record.date = dt
record.dateStr = dateStr
record.home = trim(params["team1"] or "")
record.away = trim(params["team2"] or "")
record.score = buildScoreOutput(params["score"] or "", params["aet"])
local stadium = trim(params["stadium"] or "")
local location = trim(params["location"] or "")
if stadium ~= "" and location ~= "" then
record.venue = stadium .. ", " .. location
elseif stadium ~= "" then
record.venue = stadium
else
record.venue = location
end
return record
end
local function sortMatches(matches)
table.sort(matches, function(a, b)
local ad, bd = a.date, b.date
if ad and bd then
if ad.year ~= bd.year then return ad.year < bd.year end
if ad.month ~= bd.month then return ad.month < bd.month end
if ad.day ~= bd.day then return ad.day < bd.day end
return a.sourceOrder < b.sourceOrder
elseif ad and not bd then
return true
elseif not ad and bd then
return false
else
return a.sourceOrder < b.sourceOrder
end
end)
end
local function buildTable(matches)
local out = {}
out[#out + 1] = "{{table alignment}}"
out[#out + 1] = '{| class="col1right col2center" style="width:100%" cellspacing="1"'
out[#out + 1] = "|-"
out[#out + 1] = '!style="width:25%"|'
out[#out + 1] = '!style="width:10%"|'
out[#out + 1] = '!style="width:25%"|'
out[#out + 1] = '!style="width:40%"|'
local lastKey
for _, m in ipairs(matches) do
local key
if m.date then
key = string.format("%04d-%02d-%02d", m.date.year, m.date.month, m.date.day)
else
key = "__" .. tostring(m.dateStr)
end
if key ~= lastKey then
out[#out + 1] = "|-"
out[#out + 1] = '|style="text-align:left"|' .. (m.dateStr or "")
lastKey = key
end
out[#out + 1] = '|- style="font-size:90%"'
out[#out + 1] = string.format("|%s||%s||%s||%s",
m.home or "", m.score or "", m.away or "", m.venue or "")
end
out[#out + 1] = "|}"
return table.concat(out, "\n")
end
function p.main(frame)
local args = frame.args or {}
if (not args.article or args.article == "") and frame.getParent then
local parent = frame:getParent()
if parent and parent.args then
args.article = (args.article and args.article ~= "") and args.article or parent.args.article
args[1] = args[1] or parent.args[1]
end
end
local articleTitle = args.article or args[1]
if not articleTitle or trim(articleTitle) == "" then
articleTitle = mw.title.getCurrentTitle().prefixedText
end
local title = mw.title.new(articleTitle)
if not title or not title.exists then
return '<strong class="error">Article not found: ' .. tostring(articleTitle) .. "</strong>"
end
local wikitext = title:getContent()
if not wikitext then
return '<strong class="error">Could not read content of: ' .. tostring(articleTitle) .. "</strong>"
end
local bodies = findFootballBoxInvocations(wikitext)
local matches = {}
for i, body in ipairs(bodies) do
local params = parseTemplateBody(body)
matches[#matches + 1] = buildMatchRecord(params, i)
end
if #matches == 0 then
return "<em>No football box matches found in " .. articleTitle .. ".</em>"
end
sortMatches(matches)
local wikitable = buildTable(matches)
return frame:preprocess(wikitable)
end
p._internal = {
trim = trim,
stripQuotes = stripQuotes,
findTemplateEnd = findTemplateEnd,
splitTopLevelPipes = splitTopLevelPipes,
parseTemplateBody = parseTemplateBody,
extractLabeledSection = extractLabeledSection,
extractHeaderSection = extractHeaderSection,
identifyTransclusion = identifyTransclusion,
findFootballBoxInvocations = findFootballBoxInvocations,
findNestedTemplate = findNestedTemplate,
formatDate = formatDate,
parseDateParam = parseDateParam,
buildScoreOutput = buildScoreOutput,
buildMatchRecord = buildMatchRecord,
sortMatches = sortMatches,
buildTable = buildTable,
}
return p
Content Disclaimer
Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.
- The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
- There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
- It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
- Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
- Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.