# Module:Goalscorers

require('strict');  
local yesno = require('Module:Yesno')  

local p = {}  
local g = {} -- for parameters with global scope in this module  
g.goalscorers = {} -- table where selected and sorted players will be place  
g.args = {}  
g.totalGoals = 0  
local data = {} -- module subpage data -- require('Module:Goalscorers/data/UEFA Euro 2016 qualifying');  

p.errorString = ""  
function p.error_msg  
if p.errorString ~= "" then  
return '<span>'  
-- '<code>&#124;_template=</code>'  
.. p.errorString .. '</span>';  
end  
end  
-- data for goals scored held in module subpages, e.g. "Module:Goalscorers/data/UEFA Euro 2016 qualifying"  
-- parameters containing data help in three tables  
data.rounds = {} -- group, play-off  
data.goalscorers = {} -- player, country, goals in each round)  
data.owngoalscorers = {} -- player, country, goals in each round)  
data.updated = {} -- date of latest update (month, day, year)  
---

-- ############################ Parameter handing ###############################  
p.getArgs - gets arguments from (invoke) or parent (template)  

local function getArgs  
local parents = mw.getCurrentFrame:getParent  

for k,v in pairs(parents.args) do  
--check content  
if v and v ~= "" then  
g.args[k]=mw.text.trim(v) --parents.args[k]  
end  
end  
for k,v in pairs(.args) do  
--check content  
if v and v ~= "" then  
g.args[k]= mw.text.trim(v) --parents.args[k]  
end  
end  
-- allow empty caption to blank default  
--if parents.args['caption'] then templateArgs['caption'] = parents.args['caption'] end  
end  

-- ############################## Main function and other functions ######################  
p.main - simple output of the data in the module in list form  
p.addIntroductorySentence - add sentence on number of goals and matches, with goals per match  
p.addFooterSentence - add footnote  
p.getNumberMatches  
p.owngoals - get own goals (no longer used?)  
p._owngoals - core functionality for p.owngoals  

function p.main  
getArgs  
local dataTarget = g.args[1] or g.args['data']  
if dataTarget then  
data = require('Module:Goalscorers/data/'.. dataTarget) --or 'UEFA Euro 2016 qualifying'  
return p.useModuleData -- data on goals taken from module subpage  
else  
return p.useTemplateData -- data on goals/assists taken from template  
end  

end  
function p.useModuleData  

--p.goalscorers = {} -- table where selected and sorted players will be place  
g.totalGoals = 0  
local ok = p.selectGoalscorers -- selected goalscorers meeting round and group criteris  
if not ok then return p.error_msg end  

-- CHANGE: append own goals to list (data will now include goals and own goals (negative))  
p.selectGoalscorers("OG")  

p.sortGoalscorers -- sort selected goalscorers by number of goal, then country  

local outputString = p.addIntroductorySentence .. p.outputGoalscorers .. p.addFooterSentence  
-- .. "" --TODO add intermediate heading?  
-- .. p._owngoals -- output list of goalscorers  

return p.error_msg or outputString  
end  

function p.addIntroductorySentence -- add introductory text  

local totalGoalString = "மொத்தம் " .. g.totalGoals .. " இலக்குகள் அடிக்கப்பட்டன."  
--There were [has been|have been|was|were] #GOALS goal(s) scored in #MATCHES match(s), for an average of #GOALS/#MATCHES per match.  

local matches, dateUpdated = p.getNumberMatches  
local mdyFormat = yesno(g.args['mdy'])  

local Date = require('Module:Date')._Date  

local pluralGoals = "கள்"  
local text1 = ""  
if g.totalGoals == 1 then  
pluralGoals = ""  
if dateUpdated == 'complete' then text1 = "was" else text1 = "has been" end  
else  
if dateUpdated == 'complete' then text1 = "were" else text1 = "have been" end  
end  
local text = string.format("There %s %s goal%s scored", text1, mw.getLanguage('en'):formatNum(g.totalGoals), pluralGoals)  

local pluralMatches = "es"  
if matches==1 then pluralMatches = "" end  
if matches then  
local average = g.totalGoals/tonumber(matches)  
local precision = 3 -- display d.dd (three significant disgits)  
if average < 1 then precision = 2 end -- display 0.dd (thwo significant disgits)  
average = tostring (average)  

local pluralAverage = "s"  
if tonumber(string.format("%.2f",average))==1 then pluralAverage = "" end  
text = text .. string.format(" in %d match%s, for an average of %."..precision.."g goal%s per match", matches, pluralMatches, average, pluralAverage)  
end  

if dateUpdated == 'complete' or dateUpdated == "" then  
text = text .. "."  
else  
local dateFormat = 'dmy' -- default  
if data.params and data.params['date_format'] then dateFormat = data.params['date_format'] end -- from data module  
if mdyFormat == true then dateFormat = "mdy" else  
if mdyFormat == false then dateFormat = "dmy" end -- template param overrides  
end  
text = text .. " (as of " .. Date(dateUpdated):text(dateFormat) .. ")."  
end  
text = p.addAdditionHeaderText(text, dateUpdated) -- handles template parameters bold, further, extra  

return text --totalGoalString  
end  
function p.addFooterSentence -- add notes at bottom  

local footerSentence = g.args['footer'] or ""  
--footerSentence = "This is a footer sentence." -- test footer  
if data.params then  
local footer = data.params['footer'] or nil  
if footer then  
local = mw.getCurrentFrame  
local processed = :preprocess(footer)  
if g.notes then  
footerSentence = footerSentence .. processed  
end  
end  
end  

if footerSentence ~= "" then  
footerSentence = '<div >' .. footerSentence .. '</div>'  
end  
return footerSentence  
end  
function p.getNumberMatches  
local matches = g.args['matches']  
local dateUpdated = data.updated['date'] or "1700-01-01" --'complete' -- assume completed if missing  

--local round = g.args['round'] or "all" -- round = all(empty)|group|playoffs  
--local group = g.args['group'] or "all" -- group = all(empty), A,B,C etc  
local round, group = p.getRoundAndGroup  

local allGroupGames = 0  
local latestGroupDate = "1800-01-01"  
if group and (round == "all" or group == "all") then -- count all the group games  
for k,v in pairs(data.updated.group) do  
allGroupGames = allGroupGames + v[1]  
if v[2] ~= "complete" and v[2] > latestGroupDate then latestGroupDate = v[2] end -- update if later date  
end  
if latestGroupDate == "1800-01-01" then latestGroupDate = "complete" end -- no dates so must be complete  
end  
if group and (round == "all" and group ~= "all") then -- for totals of all rounds with only one group  
allGroupGames = data.updated.group[group][1] -- number matches  
latestGroupDate = data.updated.group[group][2] -- update date or completed  
end  
if round == "all" then -- all rounds and goals  
matches=0  
for k,v in pairs(data.updated) do  
if k == "group" then  
matches = matches + allGroupGames  
if latestGroupDate ~= "complete" and latestGroupDate > dateUpdated then  
dateUpdated = latestGroupDate -- update if later date  
end  
elseif p.validateRound(k) then  
matches = matches + v[1]  
if v[2] ~= "complete" and v[2] > dateUpdated then dateUpdated = v[2] end -- update if later date  
end  

end  
elseif round == "group" then -- group round only  
if group == "all" then  
matches = allGroupGames  
dateUpdated = latestGroupDate  
else -- single group only  
matches = data.updated.group[group][1] -- number matches  
dateUpdated = data.updated.group[group][2] -- update date or completed  
end  
else -- any other round  
matches = data.updated[round][1] -- number matches  
dateUpdated = data.updated[round][2] -- update date or completed  
end  

if dateUpdated == "1700-01-01" then dateUpdated = "complete" end -- no dates so must be complete  

return matches, dateUpdated  
end  

function p.owngoals -- need to check parameters if external call  
getArgs  
data = require('Module:Goalscorers/data/'.. g.args[1]) --or 'UEFA Euro 2016 qualifying'  

local outputString = p._owngoals  
return p.error_msg or outputString  
end  
function p._owngoals -- internal call for own goals  

--p.goalscorers = {} -- table where selected and sorted players will be place  

p.selectGoalscorers("OG") -- selected goalscorers meeting round and group criteris  

p.sortGoalscorers -- sort selected goalscorers by number of goal, then country  

return p.outputGoalscorers(, "OG") -- output list of goalscorers  

end  
function p.validateRound(round)  
local validateRound = false  
for k,v in pairs(data.rounds) do  
if k == round then validateRound = true end -- data for this round exists  
end  
return validateRound  
end  

-- ############################## functions to select goalscorers ######################  
p.selectGoalscorers - select goals scoreers required for list (rounds, groups)  
p.getRoundAndGroup  
p.getGoalsCol(round) - get column containing round data or first data column if round = all  
(country, possibleGroup)  
p.getGoals (u, player)  
p.parseComment(comment)  
p.getPlayer(u)  

-- p.selectGoalscorers  
- select players meeting round and group criteria from goalscoreres list  
- gets goals and comments  

function p.selectGoalscorers(og)  

local round, group = p.getRoundAndGroup  
if not round then return false end -- exit if no valid round  

local goalMinimum = tonumber(g.args['minimum']) or -5 -- assume 5 own goals is maximum  
local goalsCol = p.getGoalsCol(round) -- first column for goals  

-- select players who have scored in rounds/groups requested  
local goalscorerData = data.goalscorers  
if og == "OG" then goalscorerData = data.owngoalscorers end  

for k,v in pairs(goalscorerData) do  
local goals, comment = 0, "" -- goals > 0 is the flag to include the player  
local playerName, playerAlias = p.getPlayer(v[1]) -- player name  
local goalsByRound, commentByRound = 0, ""  

if round == "all" then -- goals in all rounds and all groups  

for i = goalsCol, #v, 1 do  
if group and group ~= "all" and i == p.getGoalsCol("group") and group ~= p.getGroup(v[2], v[3]) then  
goalsByRound = 0  
commentByRound = ""  
else  
goalsByRound, commentByRound = p.getGoals( v[i] , playerName)  
end  

goals = goals + goalsByRound --TODO use getGoals on round options  
if commentByRound ~= "" then  
if comment == "" then  
comment = commentByRound  
else  
comment = comment .. "," .. commentByRound --TODO decide on comma or semi-colon  
end  
end  
i = i+1  
end  
elseif round == "all2" and group ~= "all" then -- goals in all rounds but only from one group  

--TODO code to go through all rounds but only include goals in specified group [TODO merge with above option]  
--mw.addWarning( g.args[1] .. ":Mix:round=all and group=" .. group .. "/" .. p.getGroup(v[2], v[3] ) )  
for i = goalsCol, #v, 1 do  
if i == p.getGoalsCol("group") and group ~= p.getGroup(v[2], v[3]) then  
goalsByRound = 0  
commentByRound = ""  
else  
goalsByRound, commentByRound = p.getGoals( v[i] , playerName)  
end  
goals = goals + goalsByRound  
if commentByRound ~= "" then  
if comment == "" then  
comment = commentByRound  
else  
comment = comment .. "," .. commentByRound --TODO decide on comma or semi-colon  
end  
end  
i = i+1  
end  
elseif round == "group" then -- group round only  
if group == p.getGroup(v[2], v[3]) then -- single group only  
goals, comment = p.getGoals( v[goalsCol] , playerName)  
elseif group == "all" then -- any group  
goals, comment = p.getGoals( v[goalsCol] , playerName)  
else  
-- do nothing for other groups  
end  
--elseif round == "playoffs" then -- playoff round (redunant?)  
-- goals = v[goalsCol]  
else -- any other round  
goals, comment = p.getGoals( v[goalsCol] , playerName) -- should also handle playoffs  
end  
if goals >= goalMinimum and goals ~= 0 then  
if comment ~= "" then  
if og == "OG" then  
comment = '<span> (' .. p.sortComment(comment) .. ')</span>'  
else  
comment = '<span>' .. comment .. '</span>' -- no parenthesis when using notes  
end  
end  

if og == "OG" then goals = -goals end -- make owngoals negative numbers  

g.goalscorers[#g.goalscorers+1] = { player=playerName, alias=playerAlias,  
country=v[2],  
goals=goals,  
comment=p.parseComment(comment)}  
--g.totalGoals = g.totalGoals + math.abs(goals) -- increment total goal counter  
end  
g.totalGoals = g.totalGoals + math.abs(goals) -- increment total goal counter  
end  
return true -- data collected for selected goalscorers  
end  
-- p.getRoundAndGroup  

function p.getRoundAndGroup  
local round = g.args['round'] or "all" -- round = all(empty)|group|playoffs  
local group = g.args['group'] or "all" -- group = all(empty), A,B,C etc  

local validateRound = false  
local validateGroupRound = false  
for k,v in pairs(data.rounds) do  
if k == round then validateRound = true end -- data for this round exists  
if k == "group" then validateGroupRound = true end -- there is a group round  
end  
if validateRound == false and round ~= "all" then  
local message = 'Invalid round "' .. round .. '" specified. No data found for that round. '  
mw.addWarning( message )  
p.errorString = p.errorString .. message  
round = nil  
end  
if validateGroupRound == false then group = false end -- there is no group round  

-- TODO add group error checking  
-- Could merge with getGoalsCol and also return goalsCol  
return round, group  
end  

-- p.getGoalsCol(round)  
- get column containing round data or first data column if round = "all"  
- allows group column to be omitted from player table when group table provided  

function p.getGoalsCol(round)  

local minimum = 1000  
if round == "all" then -- if all need column of first round  
for k,v in pairs(data.rounds) do  
if v < minimum then minimum = v end  
--return v -- return the first one [this seemed to work reliably, but sometimes table order is not as listed]  
end  
return minimum  
end  
if data.rounds and data.rounds[round] then  
return data.rounds[round] -- get column containing goals for that round  
else  
return 4 -- an old default when no data.round (may not be necessary)  
end  
end  

-- p.getGroup(country, possibleGroup)  
- get group from group table or from player table  
- possibleGroup is the column containing the Group (when no group table) or the first data column  

function p.getGroup(country, possibleGroup) -- row contain player name, country code, group if given, goals  
if data.groups then  
for k,v in pairs(data.groups) do -- iterate through the groups  
--local = gotGroup = false  
for j,u in pairs(v) do -- for each group  
if u == country then  
return k  
end  
end  
end  
return "no group found"  
else  
return possibleGroup -- no group table, so assume column three contains the group  
end  

end  
-- get number of goals and any associated comment  
the goals can be a single number (the usual case)  
or as an option table (e.g. for own goals): { number of own goals, comma-delimited list of opponents }  
- if the entry is a table, we want the first entry (a number) and the second (comment string)  
- otherwise, if a number, we just want the number and an empty string  

function p.getGoals (u, player)  
if type(u) == 'table' and type(u[1]) == 'number' then  
return u[1], u[2] -- return number of goals, comment  
elseif type(u) == 'number' then  
return u, "" -- return number of goals, empty string  
else  
p.errorString = p.errorString .. " Invalid goals entry for player " .. player  
return 0, ""  
end  
end  
function p.parseComment(comment)  

local = mw.getCurrentFrame  

-- we have something like ""  
if string.find(comment, "efn" , 1 , true ) then -- if we have a comment with a note  
g.notes = true -- set flag  
end  

return :preprocess(comment)  
end  

function p.getPlayer(u)  
if type(u) == 'table' then  
if type(u[1]) == 'string' and type(u[2]) == 'string' then  
--[[if #u[2] >1 then  
p.errorString = p.errorString .. "\n\nWe have u[1]=" .. u[1] .. " and u[2]=" .. u[2]  
end]]  
return u[1], u[2] -- return player name, player sorting alias  
else  
p.errorString = p.errorString .. " Invalid name entry for player " .. u[1] .. ", " .. u[2]  
return "", "" --TODO errroer  
end  
elseif type(u) == 'string' then  
return u, "" -- return player name  
else  
p.errorString = p.errorString .. " Invalid name entry for player " .. u or u[1] or "unknown"  
return "", ""  
end  
end  

-- ############################## functions to sort goalscorers ######################  
p.preprocessSortName (name)  
p.getPlayerSortName (playerName, sortName, countryName)  
p.sortComment(comment)  
p.getCountryName(country)  
p.sortGoalscorers -- the main sort funtion  

--[=[ function p.preprocessSortName  
stripp off wikitext and  
force to lowercase  
change special characters to standard letters  
]=]  
function p.preprocessSortName (name)  
name = string.gsub(name, "%[%[", "") -- strip off and  
name = string.gsub(name, "%]%]", "")  
--name =string.lower(name) -- force lower case and return  
name = mw.ustring.lower(name) -- use unicode function  

local specialChars = { -- list of special characters and replacement pairs  
{ "ı", "i" } , { "İ", "i" } , { "ß", "ss" },  
{ "ý", "y" } , { "ř", "r" } , { "ő", "o" },  
{ "é", "e" } , { "è", "e" } , { "þ", "th" },  
{ "ē", "e" } , { "ņ", "n" } , { "č", "c" },  
{ "ū", "u" } , { "ž", "z" } , { "æ", "ae" },  
{ "å", "a" } , { "ø", "o" } , { "ą", "a" },  
{ "ń", "n" } , { "ł", "l" } , { "ã", "a" },  
{ "ș", "s" } , { "š", "s" } , { "í", "i" },  
{ "á", "a" } , { "ä", "a" } , { "ć", "c" },  
{ "ç", "c" } , { "ğ", "g" } , { "ö", "o" },  
{ "ë", "e" } , { "ú", "u" } , { "ó", "o" },  
{ "ð", "d" } , { "ü", "u" } , { "ű", "u" },  
{ "ā", "a" } , { "ī", "i" } , { "đ", "d" },  
{ "ă", "a" } , { "â", "a" } , { "ż", "z" },  
{ "ț", "t" } , { "ş", "s" } , { "ś", "s" },  
{ "ǎ", "a" } , { "ě", "e" } , { "ů", "u" },  
{ "ĕ", "e" } , { "ñ", "n" } , { "ď", "d" },  
{ "ï", "i" } , { "ź", "z" } , { "ô", "o" },  
{ "ė", "e" } , { "ľ", "l" } , { "ģ", "g" },  
{ "ļ", "l" } , { "ę", "e" } , { "ň", "n" },  
{ "ò", "o" }  
}  
for k,v in pairs(specialChars) do -- replace special characters from supplied list  
name = string.gsub(name, v[1], v[2])  
end  

return name  
end  
--[[ return the name for sorting  
return supplied alias name for sorting  
otherwise  
checks for pipe (redirect) and uses name after pipe  
splits name into words  
returns first name if only name (e.g. Nani)  
otherwise returns name in format second_name [.. last name], firstname  
function p.getPlayerSortName (playerName, sortName, countryName)  

--dewikify all names before sorting, also forces lowercase  
playerName = p.preprocessSortName(playerName)  
sortName = p.preprocessSortName(sortName)  

if sortName ~= "" then -- if we have a sort name supplied  
return sortName -- then return it  
end  

-- players from certain countries will use name in order supplied  
local noSort = { "CAM", "CHN", "TPE", "MYA", "PRK", "KOR", "VIE" }  
for k,v in pairs(noSort) do  
if v == countryName then  
return playerName  
end  
end  

-- else work it out from the supplied player name  

-- we don't want to test the name in a redirect, so get name after pipe if there is one  
if string.find (playerName, "|") then -- test for redirect  
local names = mw.text.split( playerName, "|")  
playerName = names[2] -- get name after pipe  
end  

local names = mw.text.split( playerName, " ") -- we don't want to sort on first name  

if #names == 1 then  
return names[1] -- return name of single name player  
else  
-- we will assume the second name is the sort name e.g, Joe Bloggs, Jan van Bloggen  
local name = names[2] -- set name to second name e.g. Bloggs or van  
local i=3  
while i <= #names do -- any addition names e.g. Bloggen  
name= name .. names[i]  
i=i+1  
end  
name = name .. ", " .. names[1] -- add first name e.g. Joe or Jan  

return name -- sort on second name third name etc, first name  
end  

end  

-- sort the list of countries alphabetically  
function p.sortComment(comment)  

local items = mw.text.split( comment, ",") -- split comma-delimited list  

for k,v in pairs(items) do  
items[k] = mw.text.trim(v) -- trim spaces and coe  
end  

table.sort(items, function(a,b) return a<b end) -- sort the table alphbetically  

local list = "against " -- construct the alphabetical list string  
for i=1, #items do  
local sep = ", " -- separator for comma-delimited list  
if i==1 then sep = "" -- first word doesn't need comma  
elseif i==#items then sep = " & " -- use "and" before last word  
end  
list = list .. sep .. items[i]  
end  
return list  

end  
function p.getCountryName(country)  

if string.len(country) == 3 then -- if the country given as a three-letter code  
local codes = require('Module:Goalscorers/data/Country codes')  

for k,v in pairs(codes.alias) do  
if v[1] == country then  
return v[2]  
end  
end  
else  
return country -- return the country name as is  
end  
end  
-- sort goalscorers by goals, country and name  
the sort first sorts by number of goals  
when these are equal, it sorts by country  
when these are equal, it sorts by name  
Note: the name sort is on the first name  
- a split of the name and sort on the last name is possible  
- however, this would be complicated by Dutch (e.g. Stefan de Vrij) and Spanish names  
- would sort on second name be better  

function p.sortGoalscorers  

local sort_function = function( a,b )  
if (a.goals > b.goals) then -- primary sort on 'goals' -> a before b  
return true  
elseif (a.goals < b.goals) then -- primary sort on 'goals' -> b before a  
return false  
else -- a.goals == b.goals -- primary sort tied,  

--return a.country < b.country -- resolve with secondary sort on 'country'  
local country_a = p.getCountryName(a.country) -- sort on name of country, not the code  
local country_b = p.getCountryName(b.country)  

if (country_a < country_b) then -- secondary sort on 'country'  
return true  
elseif (country_a > country_b) then -- secondary sort on 'country'  
return false  
else -- a.country == b.country -- secondary sort tied,  

--return a.player < b.player --resolve with tertiary sort on 'player' name  

local player_a = p.getPlayerSortName(a.player, a.alias, a.country) -- get player name for sorting  
local player_b = p.getPlayerSortName(b.player, b.alias, b.country)  

return player_a < player_b --  
--[[]  
--local test_a, test_b = a.player, b.player  

-- we don't want to test the name in a redirect, so get name after pipe if there is one  
if string.find (a.player, "|") then -- test for redirect  
local names = mw.text.split( a.player, "|")  
test_a = names[2] -- get name after pipe  
end  
if string.find (b.player, "|") then  
local names = mw.text.split( b.player, "|")  
test_b = names[2]  
end  

local names_a = mw.text.split( test_a, " ") -- we don't want to sort on first name  
local names_b = mw.text.split( test_b, " ") -- so split names  

if not names_a[2] then names_a[2] = test_a end -- for players with one name  
if not names_b[2] then names_b[2] = test_b end  

return names_a[2] < names_b[2] -- sort on second name  
end  
end  
end  

table.sort(g.goalscorers, sort_function)  

end  
function p.tabulateGoalscorers(, og)  

-- ==============output the lists of goalscorers by goal======================  
local goalNumber = 1000  
local maxRank = tonumber(g.args['maxrank'] or 10) -- limit list top ten or value in parameter maxrank  
local rank = 1  
local playerCount = 0  
local rankCount = 0  
local playerCells = ""  
local firstplayerCell = ""  

local tableString = '\n{|' -- start table  
..'\n|-' .. '\n!Rank !! Player !! Goals' -- add table headers  
if g.args['header'] then tableString = tableString .. '\n|+ ' .. g.args['header'] end -- add header  
for j,u in pairs(g.goalscorers) do -- run through sorted list of selected goalscorers  

-- is the player active still?  
local playerActive = false  
if data.active_countries then  
for k,v in pairs(data.active_countries) do  
if v == u['country'] then  
playerActive = true  
break;  
end  
end  
end  
local _,roundStatus = p.getNumberMatches  
if roundStatus == "complete" then playerActive = false end -- overrides active_countries  

-- wikitext for tablulated list  
local goalscorerString = p.addLinkedIcon(, u['country']) -- linked flag icon  
if playerActive and g.args['bold']~='no' then  
goalscorerString = goalscorerString .. " **" .. u['player'] .. "**>" -- bolded name  
else  
goalscorerString = goalscorerString .. " " .. u['player'] -- name  
end  
goalscorerString = goalscorerString .. u['comment'] -- comment for o.g.  

-- we have a goalscorer  
playerCount = playerCount + 1  
rankCount = rankCount + 1  

if u['goals'] < goalNumber then -- player belongs to rowspan for new number of goals  
-- need to generate code for the previous rowspan (if there is one)  
-- then start the counts and player list for the new one  

if playerCount == 1 then  
firstplayerCell = '\n|' .. goalscorerString -- if first player in list just create cell and set goals  
goalNumber = u['goals']  
--rank = 1  
rankCount = 0  
else -- else generate previous rowspan  
local rowSpan = rankCount  
if playerCount > maxRank * 1.5 then  
firstplayerCell = '\n||' .. rankCount .. " players"  
playerCells = ""  
rowSpan = 1  
end  
tableString = tableString .. '\n|-\n||' .. rank  
--if rankCount > 1 then tableString = tableString .. "=" end -- adds equals when rank shared  
tableString = tableString .. firstplayerCell  
tableString = tableString .. '\n||' .. goalNumber  
tableString = tableString .. playerCells  

rank = rank + rankCount  
if rank > maxRank then break end -- limit list top ten or value in parameter  
rankCount = 0  
goalNumber = u['goals']  
firstplayerCell = '\n|' .. goalscorerString -- set first player cell for next rowspan  
playerCells = ""  
end  
else -- else another player with same number of goals  
playerCells = playerCells .. '\n|-' .. '\n|' .. goalscorerString -- add to player cell list  
end  
end -- reached end of list of goalscorers  

if tableString ~= "" then  
tableString = tableString .. "\n|}"  
return tableString  
else  
return ("No goals matching requested criteria.")  
end  

end  
function p.outputGoalscorers(, og) -- output list of goalscorers  
if g.args['table'] then return p.tabulateGoalscorers(, og) end -- optional table output  

local outputString = ""  
if og == "OG" then end  

-- ==============output the lists of goalscorers by goal======================  
local goalNumber = 1000  
--local goalMinimum = tonumber(templateArgs['minimum']) or 0  

local listOpen = false -- flag for list started by template  

for j,u in pairs(g.goalscorers) do -- run through sorted list of selected goalscorers  

--if u['goals'] < goalMinimum then break end -- limit list to goals over a threshold (now handled in select goalscorers)  

if u['goals'] < goalNumber then -- start new list of new number of goals  
if listOpen then -- if an open list, close last list  
outputString = outputString .. p.closeList  
listOpen = false -- redundant as will be set true again  
end  
goalNumber = u['goals']  

local goalString = " கோல்"  
--if og == "OG" then  
if goalNumber < 0 then  
goalString = " சுய" .. goalString  
end  
if math.abs(u['goals']) ~= 1 then goalString = goalString .. "s" end  

outputString = outputString .. "\n**" .. math.abs(u['goals']) .. goalString .. "**" -- list caption  

outputString = outputString .. p.openList(,og) --start new list  
listOpen = true  
--goalNumber = u['goals']  
end  
-- is the player active still?  
local playerActive = false  
if data.active_countries then  
for k,v in pairs(data.active_countries) do  
if v == u['country'] then  
playerActive = true  
break;  
end  
end  
end  
local _,roundStatus = p.getNumberMatches  
if roundStatus == "complete" then playerActive = false end -- overrides active_countries  

-- wikitext for bullet list  
local goalscorerString = '\n*<span>' .. p.addLinkedIcon(, u['country']) -- linked flag icon  
if playerActive and g.args['bold']~='no' then  
goalscorerString = goalscorerString .. " <b>" .. u['player'] .. "</b>" -- bolded name  
else  
goalscorerString = goalscorerString .. " " .. u['player'] -- name  
end  
goalscorerString = goalscorerString .. u['comment'] .. '</span>' -- comment for o.g.  

outputString = outputString .. goalscorerString -- .. " " .. tostring(u['goals'])  

end -- reached end of list of goalscorers  

if outputString ~= "" then  
outputString = outputString .. p.closeList  

return outputString  
else  
return ("No goals matching requested criteria.")  
end  
end  

-- output icon linked to national team page  
function p.addLinkedIcon(, country)  
local icon = data.templates['flag_icon_linked'] -- fbicon etc set in data module  
local level = data.templates['youth_level'] or "" -- parameter for youth level, ie under-21  
-- equivalent to  
local flagVariant = ""  
if data.templates.flagvar and data.templates.flagvar[country] then  
flagVariant = data.templates.flagvar[country]  
end  
if level ~= "" then  
return :expandTemplate{ title = icon , args = { level, country, flagVariant } }  
else  
return :expandTemplate{ title = icon , args = { country, flagVariant } } -- flag icon  
end  
end  
-- formatting of list under each number of goals  
function p.openList(,og)  

return mw.getCurrentFrame:extensionTag{  
name = 'templatestyles', args = { src = 'Div col/styles.css' }  
} .. '<div>' -- perhaps add "column-count:3;"" to limit max number of columns?  
end  
function p.closeList  
return '</div>'  
end  
function p.firstToUpper(str)  
return (str:gsub("^%l", string.upper))  
end  

-- handles parameters bold, further, extra  
function p.addAdditionHeaderText(text, dateUpdated)  
if g.args['inlineref'] then  
text = text .. g.args['inlineref']  
end  
if g.args['bold'] and g.args['bold']~='no' then  
text = text .. " Players highlighted in **bold** are still active in the competition."  
end  
if g.args['further'] then  
if text ~= "" then text = text .. " " end  
text = text .. g.args['further']  
end  
if g.args['extra'] then  
text = text .. "\n\n" .. g.args['extra']  
end  
return text  
end  
-- count number of goals for data in template  
function p.countGoals(list, number, totalGoals)  

local split = mw.text.split( list, "\n", true ) -- split the list for number of goals scorers with N goals  
local count = #split * math.abs(number) -- calculate number of goals (including own goals)  
totalGoals = totalGoals + count  

--mw.addWarning( "Entry: " .. list .. "[" .. count .. "]")  

return totalGoals  
end  

-- use data supplied by template  

--function p.list  
function p.useTemplateData  
--getArgs  

---

---
local statNumber = g.args['goals'] or g.args['assists'] or 0  
local matches = g.args['matches']  
local statType = "goal"  
if g.args['assists'] then statType = "assist" end  
if g.args['clean sheets'] then statType = "clean sheet" end  
local ongoing = g.args['ongoing']  
local text1 = "There"  
if g.args['lc'] then text1 = "there" end  
local text2 = "were"  
if ongoing then text2 = "have been" end  
local updateString = ""  
local averageString = ""  
local goalPlural = "s" -- goal(s)  
if g.args['goals'] and tonumber(g.args['goals']) == 1 then  
goalPlural = ""  
text2 = "was"  
if ongoing then text2 = "has been" end  
end  
local matchPlural = "es" -- match(es)  
if g.args['matches'] and tonumber(g.args['matches']) == 1 then matchPlural = "" end  

-- auto version: string.format(" in %d match%s, for an average of %."..precision.."g goal%s per match", matches, pluralMatches, average, pluralAverage)  
if g.args['goals'] and g.args['matches'] then  
local averageGoals = g.args['goals']/g.args['matches']  
local avGoalPlural = "s"  
if averageGoals == 1 then avGoalPlural = "" end  
averageString = string.format(" in %d match%s, for an average of %.3g goal%s per match", g.args['matches'], matchPlural, averageGoals, avGoalPlural)  
end  
if g.args['updated'] and g.args['updated'] ~= "complete" then  
updateString = "&nbsp;(as of " ..g.args['updated'] .. ")"  
end  
local sep = "."  
if g.args['sep'] then sep = g.args['sep'] end  
local text = ""  
if g.args['goals'] then  
text = string.format("%s %s %d %s%s scored%s",  
text1, text2, statNumber, statType, goalPlural, averageString..updateString..sep)  
end  
text = p.addAdditionHeaderText(text) -- handles template parameters bold, further, extra  

--**30 **  
<div>  
}</div>|}}  
local output = "\n"  
local number = 30  

local totalGoals = 0  

while number > -4 do -- for the each goals/assists  

local entry = g.args[number .. ' goals'] or g.args[number .. ' goal']  
or g.args[number .. ' assists'] or g.args[number .. ' assist']  
or g.args[number .. ' clean sheets'] or g.args[number .. ' clean sheet']  

if number < 0 then  
entry = g.args[math.abs(number) .. ' own goals'] or g.args[math.abs(number) .. ' own goal']  
statType = "own goal"  
end  
local plural = "s"  
if number == 1 or number == -1 then plural = "" end  

if entry then -- do we have goals/assists for this number  

output = output .. "\n**" .. tostring(math.abs(number)) .. " " .. statType .. plural .. "**\n"  
.. p.openList .. "\n" .. entry .. p.closeList  
totalGoals = p.countGoals(entry, number, totalGoals)  
end  

number = number -1  
end  

if statType == "goal" or statType == "own goal" then  
if g.args['goals'] and totalGoals ~= tonumber(g.args['goals']) then  
mw.addWarning("WARNING. Mismatch between number of goals listed (" .. totalGoals .. ") and goals parameter (" .. g.args['goals'] .. ").")  
end  
end  

---
local footerText = g.args['footer-text'] or g.args['bottom'] or ""  
local footerHeading = g.args['footer-heading'] or g.args['bottom-text'] or ""  
local footer = ""  
if footerText ~= "" then  
local heading = ""  
if footerHeading ~= "" then  
heading = '<p>' .. footerHeading .. '</p>'  
end  
footer = '\n' .. heading .. p.openList .. '\n' .. footerText .. p.closeList  
end  

---
local source = g.args['source'] or ""  
if source ~= "" then source = "<small>Source: " .. source .. "</small>" end  

return text .. output .. footer .. source  
end  
return p