Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Main page
Recent changes
Random page
Help about MediaWiki
Special pages
Cultopedia
Search
Search
Appearance
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Module:Footballer positions
Module
Discussion
English
Read
Edit
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
View history
General
What links here
Related changes
Page information
Appearance
move to sidebar
hide
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
local p = {} local mHList = require('Module:List') -- Position definitions: {names/abbrevs}, link, display -- Use %D for dash placeholder, %C for centre/center placeholder local positionDefs = { -- Goalkeepers {{"goalkeeper", "goalie", "keeper", "gk"}, "Goalkeeper (association football)", "goalkeeper"}, -- Defenders {{"defender", "defence", "defense", "back", "df"}, "Defender (association football)", "defender"}, {{"centreback", "centralback", "cb"}, "Defender (association football)#Centre-back", "%Ccentre%Dback"}, {{"centredefender"}, "Defender (association football)#Centre-back", "%Ccentre-back"}, {{"centraldefender", "centraldefence", "centraldefense", "cd"}, "Defender (association football)#Centre-back", "central defender"}, {{"centrehalf"}, "Defender (association football)#Centre-back", "%Ccentre%Dhalf"}, {{"fullback", "outsideback", "fb"}, "Defender (association football)#Full-back", "full%Dback"}, {{"wingback", "wb"}, "Defender (association football)#Wing-back", "wing%Dback"}, {{"sweeper", "sw"}, "Defender (association football)#Sweeper", "sweeper"}, {{"libero"}, "Defender (association football)#Sweeper", "libero"}, {{"rightback", "rightdefender", "rb"}, "Defender (association football)#Full-back", "right%Dback"}, {{"rightfullback"}, "Defender (association football)#Full-back", "right full%Dback"}, {{"leftback", "leftdefender", "lb"}, "Defender (association football)#Full-back", "left%Dback"}, {{"leftfullback"}, "Defender (association football)#Full-back", "left full%Dback"}, {{"rightwingback", "rwb"}, "Defender (association football)#Wing-back", "right wing%Dback"}, {{"leftwingback", "lwb"}, "Defender (association football)#Wing-back", "left wing%Dback"}, -- Midfielders {{"midfielder", "midfield", "mf"}, "Midfielder", "midfielder"}, {{"halfback", "hb"}, "Midfielder", "half%Dback"}, {{"centralmidfielder", "centralmidfield", "cm"}, "Midfielder#Central midfielder", "central midfielder"}, {{"centremidfielder", "centremidfield", "centremid", "centermid"}, "Midfielder#Central midfielder", "%Ccentre midfielder"}, {{"defensivemidfielder", "defendingmidfielder", "defensivemidfield", "dm"}, "Midfielder#Defensive midfielder", "defensive midfielder"}, {{"attackingmidfielder", "attackingmidfield", "offensivemidfielder", "offensivemidfield", "am"}, "Midfielder#Attacking midfielder", "attacking midfielder"}, {{"widemidfielder", "sidemidfielder", "wm"}, "Midfielder#Wide midfielder", "wide midfielder"}, {{"winger", "wi"}, "Midfielder#Winger", "winger"}, {{"wing"}, "Midfielder#Winger", "wing"}, {{"winghalf", "winghalfback", "wh"}, "Midfielder#Wing-half", "wing%Dhalf"}, {{"centraldefensivemidfielder", "cdm"}, "Midfielder#Defensive midfielder", "central defensive midfielder"}, {{"centredefensivemidfielder"}, "Midfielder#Defensive midfielder", "%Ccentre defensive midfielder"}, {{"leftdefensivemidfielder", "ldm"}, "Midfielder#Defensive midfielder", "left defensive midfielder"}, {{"rightdefensivemidfielder", "rdm"}, "Midfielder#Defensive midfielder", "right defensive midfielder"}, {{"centralattackingmidfielder", "cam"}, "Midfielder#Attacking midfielder", "central attacking midfielder"}, {{"centreattackingmidfielder"}, "Midfielder#Attacking midfielder", "%Ccentre attacking midfielder"}, {{"leftattackingmidfielder", "lam"}, "Midfielder#Attacking midfielder", "left attacking midfielder"}, {{"rightattackingmidfielder", "ram"}, "Midfielder#Attacking midfielder", "right attacking midfielder"}, {{"holdingmidfielder", "holdingmidfield", "hm"}, "Midfielder#Defensive midfielder", "holding midfielder"}, {{"rightmidfielder", "rightmidfield", "rm"}, "Midfielder#Wide midfielder", "right midfielder"}, {{"leftmidfielder", "leftmidfield", "lm"}, "Midfielder#Wide midfielder", "left midfielder"}, {{"rightwinger", "rw"}, "Midfielder#Winger", "right winger"}, {{"rightwing"}, "Midfielder#Winger", "right wing"}, {{"leftwinger", "lw"}, "Midfielder#Winger", "left winger"}, {{"leftwing"}, "Midfielder#Winger", "left wing"}, {{"oldcentrehalf", "centrehalfback", "ch"}, "Midfielder#Centre-half", "%Ccentre%Dhalf"}, {{"righthalf", "righthalfback", "rightwinghalf", "rh"}, "Midfielder#Wing-half", "right%Dhalf"}, {{"lefthalf", "lefthalfback", "leftwinghalf", "lh"}, "Midfielder#Wing-half", "left%Dhalf"}, -- Forwards {{"forward", "attacker", "attack", "fw"}, "Forward (association football)", "forward"}, {{"centreforward", "centralforward", "cf"}, "Forward (association football)#Centre-forward", "%Ccentre%Dforward"}, {{"striker", "st"}, "Forward (association football)#Striker", "striker"}, {{"secondstriker", "secondarystriker", "secondforward", "ss"}, "Forward (association football)#Second striker", "second striker"}, {{"supportingstriker", "supportstriker"}, "Forward (association football)#Second striker", "supporting striker"}, {{"deeplyingstriker", "deeplyingforward"}, "Forward (association football)#Second striker", "deep-lying striker"}, {{"insideforward", "if"}, "Forward (association football)#Inside forward", "inside forward"}, {{"outsideforward", "of"}, "Forward (association football)#Outside forward", "outside forward"}, {{"rightforward", "rf"}, "Forward (association football)", "right forward"}, {{"leftforward", "lf"}, "Forward (association football)", "left forward"}, {{"insideright", "rightinsideforward", "ir"}, "Forward (association football)#Inside forward", "inside right"}, {{"insideleft", "leftinsideforward", "il"}, "Forward (association football)#Inside forward", "inside left"}, {{"outsideright", "rightoutsideforward", "or"}, "Forward (association football)#Outside forward", "outside right"}, {{"outsideleft", "leftoutsideforward", "ol"}, "Forward (association football)#Outside forward", "outside left"}, -- Utility player {{"utilityplayer", "utility", "ut"}, "Utility player#Association football", "utility player"}, } -- Build lookup table from definitions local positionData = {} for _, def in ipairs(positionDefs) do local names, link, display = def[1], def[2], def[3] local data = {link, display} for _, name in ipairs(names) do positionData[name] = data end end -- Normalize HTML entities local function decodeEntities(str) str = str:gsub(" ", " ") str = mw.text.decode(str, true) return str end -- Delink wikilinks local function delink(str) return str:gsub("%[%[(.-)%]%]", function(match) local pipePos = match:find("|") if pipePos then return match:sub(pipePos + 1) else return match end end) end -- Extract reference placeholders from end of string local function extractRefs(str) local refs = "" -- Pattern for MediaWiki strip markers local refPattern = "\127'\"`UNIQ%-%-ref.-QINU`\"'\127" while true do local s, e = str:find(refPattern .. "%s*$") if s then refs = str:sub(s, e):gsub("%s*$", "") .. refs str = str:sub(1, s - 1) else break end end return mw.text.trim(str), refs end -- Normalize for lookup: lowercase, remove dashes/spaces, convert center->centre local function normalizeForLookup(str) local s = str:lower() s = s:gsub("[-βββ]", "") s = s:gsub(" ", "") s = s:gsub("center", "centre") return s end -- Detect original formatting for centre/center and dash/space local function detectFormatting(original, normalized) local usesCenter = original:lower():find("center") ~= nil -- Check for dash variants local dashChar = original:match("%-") or original:match("β") or original:match("β") or original:match("β") -- If no dash found, determine if space or no separator was used -- Compare lengths: if original (lowercased, center->centre) is longer than normalized, -- there must be spaces local spaceInsteadOfDash = false local noSeparator = false if not dashChar then local comparable = original:lower():gsub("center", "centre") if #comparable > #normalized then -- Original has extra characters (spaces) spaceInsteadOfDash = true elseif #comparable == #normalized and #normalized > 1 then -- Same length means no separators were used -- But only set noSeparator if the input looks like a full word (not an abbreviation) -- Abbreviations are short (2-3 chars) and all letters local isAbbreviation = original:match("^%a%a%a?$") ~= nil if not isAbbreviation then noSeparator = true end -- If it's an abbreviation, we fall through to the default dash behavior end end return usesCenter, dashChar, spaceInsteadOfDash, noSeparator end -- Apply formatting to display text local function applyFormatting(display, usesCenter, dashChar, spaceInsteadOfDash, noSeparator) -- Handle %C placeholder (centre/center) if usesCenter then display = display:gsub("%%Ccentre", "center") else display = display:gsub("%%C", "") end -- Handle %D placeholder (dash) if dashChar then display = display:gsub("%%D", "-") elseif spaceInsteadOfDash then display = display:gsub("%%D", " ") elseif noSeparator then display = display:gsub("%%D", "") else display = display:gsub("%%D", "-") end return display end -- Process a single position local function processPosition(pos, isFirst, trackingNeeded, linkTargetReplacements, citationNeededTags) local backup = pos -- Extract citation needed placeholders and references from end of position local citationNeeded = "" local refs = "" local cnPlaceholderPattern = "\127CITATIONNEEDED%d+\127" local refPattern = "\127'\"`UNIQ%-%-ref.-QINU`\"'\127" while true do local cnStart, cnEnd = pos:find(cnPlaceholderPattern .. "%s*$") local refStart, refEnd = pos:find(refPattern .. "%s*$") if cnStart and (not refStart or cnStart > refStart) then -- Citation needed is at the very end local placeholder = pos:sub(cnStart, cnEnd):gsub("%s*$", "") citationNeeded = (citationNeededTags[placeholder] or "") .. citationNeeded pos = pos:sub(1, cnStart - 1) elseif refStart then -- Ref is at the very end refs = pos:sub(refStart, refEnd):gsub("%s*$", "") .. refs pos = pos:sub(1, refStart - 1) else break end end pos = mw.text.trim(pos) -- Handle special centre-half wikilink local lowerPos = pos:lower() if lowerPos:match("^%[%[midfielder#centre%-half|cent[re][re]%-?%s?half%]%]$") then pos = "old centre-half" end -- Delink pos = delink(pos) pos = mw.text.trim(pos) -- Lookup local normalized = normalizeForLookup(pos) local usesCenter, dashChar, spaceInsteadOfDash, noSeparator = detectFormatting(pos, normalized) local data = positionData[normalized] local result local usedBackup = false if data then local link, display = data[1], data[2] display = applyFormatting(display, usesCenter, dashChar, spaceInsteadOfDash, noSeparator) -- Capitalize first position, lowercase others if isFirst then display = display:sub(1, 1):upper() .. display:sub(2):lower() else display = display:lower() end -- Simplify wikilink if link target equals display text (case insensitive) if link:lower() == display:lower() then result = "[[" .. display .. "]]" else result = "[[" .. link .. "|" .. display .. "]]" end else -- No match found - check if this came from a link target replacement if linkTargetReplacements and linkTargetReplacements[pos] then -- Use the original link with separator result = linkTargetReplacements[pos] else -- Use backup, but restore any citation needed placeholders in it first result = backup for placeholder, tag in pairs(citationNeededTags) do result = result:gsub(placeholder, tag) end usedBackup = true -- Set the flag end trackingNeeded[1] = true end -- Re-add references if refs ~= "" and not usedBackup then result = result .. refs end -- Re-add citation needed only if we didn't use the backup -- (backup already contains the citation needed tag) if citationNeeded ~= "" and not usedBackup then result = result .. citationNeeded end return result end function p.main(frame) local args = frame.args local input = args[1] -- If called from a template, get parent args if input == nil or input == "" then args = frame:getParent().args input = args[1] end input = input or "" -- No input = no output if input == "" or mw.text.trim(input) == "" then return "" end -- Normalize HTML entities input = decodeEntities(input) -- Trim input = mw.text.trim(input) -- Handle hlist/plainlist input local tsPattern = "\127'\"`UNIQ%-%-templatestyles%-.-%QINU`\"'\127%s*<div class=\"" local listMatch = input:match(tsPattern .. "hlist[^\"]*\"[^>]*>(.-)</div>") or input:match(tsPattern .. "plainlist[^\"]*\"[^>]*>(.-)</div>") if listMatch then local listPositions = {} -- Check for HTML list format (<ul><li>...</li></ul>) if listMatch:match('<ul>') then for item in listMatch:gmatch('<li>(.-)</li>') do item = mw.text.trim(item) if item ~= "" then table.insert(listPositions, item) end end else -- Wikitext list format (* item) for item in listMatch:gmatch('%*%s*([^\n]+)') do item = mw.text.trim(item) if item ~= "" then table.insert(listPositions, item) end end end -- If we found list items, replace input with comma-separated positions if #listPositions > 0 then input = table.concat(listPositions, ", ") end end -- Break tag pattern local brPattern = "<[bB][rR]%s*/?%s*>" -- Handle and/or adjacent to break tags (convert to just /) input = input:gsub("%s+[Aa][Nn][Dd]%s*" .. brPattern, "/") input = input:gsub("%s+[Oo][Rr]%s*" .. brPattern, "/") input = input:gsub(brPattern .. "%s*[Aa][Nn][Dd]%s+", "/") input = input:gsub(brPattern .. "%s*[Oo][Rr]%s+", "/") -- Replace remaining br tags with / input = input:gsub(brPattern, "/") -- Extract citation needed templates and replace with placeholders -- Match sup tags with Template-Fact class (citation needed) -- Categories may precede the sup tag, so we handle them together local citationNeededTags = {} local citationNeededIndex = 0 -- Process from end to start to avoid position shifting issues -- First collect all matches, then replace local matches = {} local searchPos = 1 while true do -- Find next sup tag with Template-Fact local supStart, supEnd = input:find("<sup[^>]*Template%-Fact[^>]*>.-</sup>", searchPos) if not supStart then break end -- Look backwards for any immediately preceding categories local catStart = supStart local checkPos = supStart while checkPos > 1 do -- Get the prefix before current position local prefix = input:sub(1, checkPos - 1) -- Try to match a category at the end of prefix (with optional trailing whitespace) local catMatchStart, catMatchEnd = prefix:find("%[%[Category:[^%]]+%]%]%s*$") if catMatchStart then catStart = catMatchStart checkPos = catMatchStart else break end end -- Assign index now (during collection) to preserve order citationNeededIndex = citationNeededIndex + 1 table.insert(matches, {catStart, supEnd, citationNeededIndex}) searchPos = supEnd + 1 end -- Replace matches from end to start to preserve positions for i = #matches, 1, -1 do local startPos, endPos, idx = matches[i][1], matches[i][2], matches[i][3] local fullMatch = input:sub(startPos, endPos) local placeholder = "\127CITATIONNEEDED" .. idx .. "\127" citationNeededTags[placeholder] = fullMatch input = input:sub(1, startPos - 1) .. placeholder .. input:sub(endPos + 1) end -- Check for tags if input:match("<[^>]+>") then -- Add tracking category local category = frame:expandTemplate{ title = "main other", args = {"[[Category:Pages using infobox football biography with tags in position parameter]]"} } -- Restore citation needed tags before returning for placeholder, tag in pairs(citationNeededTags) do input = input:gsub(placeholder, tag) end return input .. category end -- Replace remaining line breaks with / input = input:gsub("\n", "/") -- Check for separators in wikilink display text and delink if found local hadSeparatorInDisplayText = false local originalInputBeforeDelink = input local separatorPattern = "[/,;+&]" local hasWordSeparator = function(text) return text:match("%s+[Aa][Nn][Dd]%s+") or text:match("%s+[Oo][Rr]%s+") end local linkTargetReplacements = {} -- Track original links that had separators in target -- Check each wikilink for separators in display text for wikilink in input:gmatch("%[%[.-%]%]") do local pipePos = wikilink:find("|") local textToCheck if pipePos then -- Has display text - check the part after the pipe textToCheck = wikilink:sub(pipePos + 1, -3) -- Extract display text (excluding ]]) -- Also check if separator is in the link target (before pipe) local linkTarget = wikilink:sub(3, pipePos - 1) if linkTarget:match(separatorPattern) or hasWordSeparator(linkTarget) then -- Store the original link and its display text for later restoration if needed local displayText = textToCheck linkTargetReplacements[displayText] = wikilink -- Remove separators from the link target in the input local cleanedTarget = linkTarget:gsub(separatorPattern, ""):gsub("%s+and%s+", ""):gsub("%s+or%s+", "") input = input:gsub("%[%[" .. linkTarget:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") .. "|" .. displayText:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") .. "%]%]", "[[" .. cleanedTarget .. "|" .. displayText .. "]]") end else -- No pipe - check the link target itself (excluding [[]]) textToCheck = wikilink:sub(3, -3) end if textToCheck:match(separatorPattern) or hasWordSeparator(textToCheck) then hadSeparatorInDisplayText = true end end -- If separator found in display text, delink the input if hadSeparatorInDisplayText then input = delink(input) end -- Convert word separators to / before splitting, but only outside of wikilinks -- First, temporarily replace wikilinks with placeholders local wikilinks = {} local placeholderIndex = 0 input = input:gsub("%[%[.-%]%]", function(wikilink) placeholderIndex = placeholderIndex + 1 local placeholder = "\127WIKILINK" .. placeholderIndex .. "\127" wikilinks[placeholder] = wikilink return placeholder end) -- Now convert word separators input = input:gsub("%s+[Aa][Nn][Dd]%s+", "/") input = input:gsub("%s+[Oo][Rr]%s+", "/") -- Restore wikilinks for placeholder, wikilink in pairs(wikilinks) do input = input:gsub(placeholder, wikilink) end -- Split by separators local positions = {} local refPattern = "\127'\"`UNIQ%-%-ref.-QINU`\"'\127" -- First, split by separators local rawParts = {} local lastEnd = 1 for sepStart, sepEnd in input:gmatch("()%s*" .. separatorPattern .. "%s*()") do table.insert(rawParts, input:sub(lastEnd, sepStart - 1)) lastEnd = sepEnd end table.insert(rawParts, input:sub(lastEnd)) -- Now process parts: if a part starts with ref(s) or citation needed, move them to previous position local cnPlaceholderPattern = "\127CITATIONNEEDED%d+\127" for i, part in ipairs(rawParts) do local leadingRefs = "" local remainder = part -- Extract leading refs and citation needed placeholders while true do local refStart, refEnd = remainder:find("^%s*" .. refPattern) local cnStart, cnEnd = remainder:find("^%s*" .. cnPlaceholderPattern) if refStart then leadingRefs = leadingRefs .. remainder:sub(refStart, refEnd) remainder = remainder:sub(refEnd + 1) elseif cnStart then leadingRefs = leadingRefs .. remainder:sub(cnStart, cnEnd) remainder = remainder:sub(cnEnd + 1) else break end end -- Attach leading refs/citation needed to previous position if leadingRefs ~= "" and #positions > 0 then positions[#positions] = positions[#positions] .. leadingRefs end -- Add remainder as new position (if not empty) remainder = mw.text.trim(remainder) if remainder ~= "" then table.insert(positions, remainder) end end -- If no positions found, treat whole input as one position if #positions == 0 then positions = {mw.text.trim(input)} end -- Filter out management/executive positions local excludeWords = {'retired', 'unknown', '?', 'manager', 'coach', 'trainer', 'director', 'president', 'executive', 'chairman', 'ceo', 'referee'} local filteredPositions = {} local hasInvalidPosition = false for _, pos in ipairs(positions) do local lowerPos = pos:lower() local exclude = false for _, word in ipairs(excludeWords) do if lowerPos:find(word, 1, true) then exclude = true hasInvalidPosition = true break end end if not exclude then table.insert(filteredPositions, pos) end end positions = filteredPositions -- Add tracking category for invalid positions local invalidCategory = "" if hasInvalidPosition then invalidCategory = frame:expandTemplate{ title = "main other", args = {"[[Category:Pages using infobox football biography with invalid position]]"} } mw.addWarning('<span style="color:#d33">Error: One or more of the position(s) listed in the infobox parameter is not an actual role for a footballer\'s playing career.</span>') end -- If all positions were filtered out, return only categories if #positions == 0 then return invalidCategory end -- Process each position local trackingNeeded = {false} local results = {} for i, pos in ipairs(positions) do table.insert(results, processPosition(pos, i == 1, trackingNeeded, linkTargetReplacements, citationNeededTags)) end -- Build irregular category if needed local irregularCategory = "" if trackingNeeded[1] then irregularCategory = frame:expandTemplate{ title = "main other", args = {"[[Category:Pages using infobox football biography with irregular position]]"} } mw.addWarning('<span style="color:#d33">Error: One or more of the position(s) listed in the infobox parameter is not a typical position in football.</span>') end -- Special handling: if we delinked due to separator in display text, -- and ANY position didn't match, return original input unchanged if hadSeparatorInDisplayText and trackingNeeded[1] then return originalInputBeforeDelink .. invalidCategory .. irregularCategory end -- Build output local output if #results == 1 then output = results[1] else -- Use Module:List hlist output = mHList.makeList('horizontal', results) end return output .. invalidCategory .. irregularCategory end return p
Summary:
Please note that all contributions to Cultopedia may be edited, altered, or removed by other contributors. If you do not want your writing to be edited mercilessly, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource (see
Cultopedia:Copyrights
for details).
Do not submit copyrighted work without permission!
Cancel
Editing help
(opens in new window)
Template used on this page:
Module:Footballer positions/doc
(
edit
)
Search
Search
Editing
Module:Footballer positions
Add topic