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 = &quot;&quot;
 function p.error_msg()
 	if p.errorString ~= &quot;&quot; then
 		return '&lt;span style=&quot;font-size:100%&quot; class=&quot;error&quot;&gt;'
 	         -- '&lt;code style=&quot;color:inherit;border:inherit;padding:inherit;&quot;&gt;&amp;#124;_template=&lt;/code&gt;'
 	         .. p.errorString .. '&lt;/span&gt;';
 	end
 end
 -- data for goals scored held in module subpages, e.g. &quot;Module:Goalscorers/data/UEFA Euro 2016 qualifying&quot;
       --[[ 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 frame (invoke) or parent frame (template)     
 ]]
 
 local function getArgs(frame)
 	local parents = mw.getCurrentFrame():getParent()
 		
 	for k,v in pairs(parents.args) do
 		--check content
 		if v and v ~= &quot;&quot; then
 			g.args[k]=mw.text.trim(v) --parents.args[k]
 		end
 	end
 	for k,v in pairs(frame.args) do
 		--check content
 		if v and v ~= &quot;&quot; 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(frame)
     getArgs(frame)
     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(frame)  -- data on goals taken from module subpage
     else
     	return p.useTemplateData(frame)  -- data on goals/assists taken from template
     end
     
 end
 function p.useModuleData(frame)
 
     --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(&quot;OG&quot;)    
     
     p.sortGoalscorers() -- sort selected goalscorers by number of goal, then country
 
     local outputString = p.addIntroductorySentence() .. p.outputGoalscorers(frame) .. p.addFooterSentence()
 --                      .. &quot;&quot;              --TODO add intermediate heading?
 --                      .. p._owngoals(frame)  -- output list of goalscorers
     
     return p.error_msg() or outputString
 end
 
 function p.addIntroductorySentence()          -- add introductory text
 	
 	local totalGoalString = &quot;மொத்தம் &quot; .. g.totalGoals .. &quot; இலக்குகள் அடிக்கப்பட்டன.&quot;
 	--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 = &quot;கள்&quot;
 	local text1 = &quot;&quot;
 	if g.totalGoals == 1 then
 		pluralGoals = &quot;&quot;
 		if dateUpdated == 'complete' then text1 = &quot;was&quot; else text1 = &quot;has been&quot; end
 	else
 		if dateUpdated == 'complete' then text1 = &quot;were&quot; else text1 = &quot;have been&quot; end
 	end
 	local text = string.format(&quot;There %s %s goal%s scored&quot;, text1, mw.getLanguage('en'):formatNum(g.totalGoals), pluralGoals)
 	
 	local pluralMatches = &quot;es&quot;
 	if matches==1 then pluralMatches = &quot;&quot; end
 	if matches then
 		local average = g.totalGoals/tonumber(matches)
 		local precision = 3                        -- display d.dd (three significant disgits)
 		if average &lt; 1 then precision = 2 end      -- display 0.dd (thwo significant disgits)
 		average = tostring (average)
 
 		local pluralAverage = &quot;s&quot;
 		if  tonumber(string.format(&quot;%.2f&quot;,average))==1 then pluralAverage = &quot;&quot; end
 	    text = text .. string.format(&quot; in %d match%s, for an average of %.&quot;..precision..&quot;g goal%s per match&quot;, matches, pluralMatches, average, pluralAverage)
 	end
     
 	if dateUpdated == 'complete' or dateUpdated == &quot;&quot; then
 	    text = text .. &quot;.&quot;
 	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 = &quot;mdy&quot; else
 			if mdyFormat == false then dateFormat = &quot;dmy&quot; end   -- template param overrides
 		end
 	    text = text .. &quot; (as of &quot; .. Date(dateUpdated):text(dateFormat) .. &quot;).&quot;
 	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 &quot;&quot;
     --footerSentence = &quot;This is a footer sentence.&quot;               -- test footer
     if data.params then
     	local footer = data.params['footer'] or nil
 	    if footer then
 	    	local frame = mw.getCurrentFrame()
 	    	local processed = frame:preprocess(footer)
 	    	if g.notes then
 	    		footerSentence  = footerSentence  .. processed
 	    	end
 	    end
     end
     
     if footerSentence ~= &quot;&quot; then
     	footerSentence = '&lt;div style = &quot;&quot; &gt;' .. footerSentence .. '&lt;/div&gt;'
     end
     return footerSentence
 end
 function p.getNumberMatches()
    	local matches = g.args['matches']
    	local dateUpdated = data.updated['date'] or &quot;1700-01-01&quot; --'complete' -- assume completed if missing
   
     --local round = g.args['round'] or &quot;all&quot;    -- round =  all(empty)|group|playoffs
     --local group = g.args['group'] or &quot;all&quot;     -- group =  all(empty), A,B,C etc 
     local round, group =  p.getRoundAndGroup()
     
     local allGroupGames = 0
     local latestGroupDate = &quot;1800-01-01&quot; 
     if group and (round == &quot;all&quot; or group == &quot;all&quot;) then           -- count all the group games
     	for k,v in pairs(data.updated.group) do
     		allGroupGames = allGroupGames + v[1]
     		if v[2] ~= &quot;complete&quot; and v[2] &gt; latestGroupDate then latestGroupDate = v[2] end -- update if later date
     	end
     	if latestGroupDate == &quot;1800-01-01&quot; then latestGroupDate = &quot;complete&quot;  end -- no dates so must be complete
     end
     if group and (round == &quot;all&quot; and group ~= &quot;all&quot;) 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 == &quot;all&quot; then                                       -- all rounds and goals
         matches=0
         for k,v in pairs(data.updated) do 
             if k == &quot;group&quot; then
         		matches = matches + allGroupGames
     	     	if latestGroupDate ~= &quot;complete&quot; and latestGroupDate &gt; dateUpdated then 
     	     		dateUpdated = latestGroupDate                -- update if later date
     	        end 
         	elseif p.validateRound(k) then
         		matches = matches + v[1]
     		    if v[2] ~= &quot;complete&quot; and v[2] &gt; dateUpdated then dateUpdated = v[2] end -- update if later date
         	end
         	
         end 
 	elseif round == &quot;group&quot; then                                  -- group round only
 	    if group == &quot;all&quot; 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 == &quot;1700-01-01&quot; then dateUpdated = &quot;complete&quot;  end -- no dates so must be complete
 
    	return matches, dateUpdated
 end
 
 function p.owngoals(frame) -- need to check parameters if external call
     getArgs(frame)
     data = require('Module:Goalscorers/data/'.. g.args[1]) --or 'UEFA Euro 2016 qualifying' 
 
     local outputString = p._owngoals(frame)
     return  p.error_msg() or outputString
 end
 function p._owngoals(frame) -- internal call for own goals
 
     --p.goalscorers = {} -- table where selected and sorted players will be place
     
     p.selectGoalscorers(&quot;OG&quot;) -- selected goalscorers meeting round and group criteris
     
     p.sortGoalscorers() -- sort selected goalscorers by number of goal, then country
 
     return p.outputGoalscorers(frame, &quot;OG&quot;) -- 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 == &quot;OG&quot; then goalscorerData = data.owngoalscorers end
     
     for k,v in pairs(goalscorerData) do
         local goals, comment = 0, &quot;&quot;                         -- goals &gt; 0 is the flag to include the player
         local playerName, playerAlias = p.getPlayer(v[1])                -- player name
         local goalsByRound, commentByRound = 0, &quot;&quot;
 		
 		if round == &quot;all&quot;  then                         -- goals in all rounds and all groups
 		    
 		    for i = goalsCol, #v, 1 do        
 		    	if group and group ~= &quot;all&quot; and i == p.getGoalsCol(&quot;group&quot;) and group ~= p.getGroup(v[2], v[3])  then 
 		    	   goalsByRound = 0
 		    	   commentByRound = &quot;&quot;
 		    	else
 		    		goalsByRound, commentByRound = p.getGoals( v[i] , playerName)
 		    	end
 
 		    	goals = goals +   goalsByRound              --TODO use getGoals on round options
 		    	if commentByRound ~= &quot;&quot; then
 			    	if comment == &quot;&quot; then
 			    		comment = commentByRound 
 			    	else
 			    		comment = comment .. &quot;,&quot; .. commentByRound  --TODO decide on comma or semi-colon
 		    		end
 	       		end
 		    	i = i+1
 		    end
 		elseif round == &quot;all2&quot; and group ~= &quot;all&quot; 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] .. &quot;:Mix:round=all and group=&quot; .. group .. &quot;/&quot; .. p.getGroup(v[2], v[3] ) )
 		    for i = goalsCol, #v, 1 do        
 		    	if i == p.getGoalsCol(&quot;group&quot;) and group ~= p.getGroup(v[2], v[3])  then 
 		    	   goalsByRound = 0
 		    	   commentByRound = &quot;&quot;
 		    	else
 		    		goalsByRound, commentByRound = p.getGoals( v[i] , playerName)
 		    	end
 		    	goals = goals +   goalsByRound              
 		    	if commentByRound ~= &quot;&quot; then
 			    	if comment == &quot;&quot; then
 			    		comment = commentByRound 
 			    	else
 			    		comment = comment .. &quot;,&quot; .. commentByRound  --TODO decide on comma or semi-colon
 		    		end
 	       		end
 		    	i = i+1
 		    end
 		elseif round == &quot;group&quot; 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 == &quot;all&quot; then                                 -- any group
 				goals, comment = p.getGoals( v[goalsCol] , playerName)
 			else   
 				-- do nothing for other groups
 			end
 		--elseif round == &quot;playoffs&quot; 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 &gt;= goalMinimum and goals ~= 0 then
 	    	   if comment ~= &quot;&quot; then 
 	    	   	  if og == &quot;OG&quot; then 
 	    	   	  	comment = '&lt;span&gt; (' .. p.sortComment(comment) .. ')&lt;/span&gt;' 
 	    	   	  else
 	    	   	  	comment = '&lt;span&gt;' .. comment .. '&lt;/span&gt;'   -- no parenthesis when using notes
 	    	      end
 	    	   end
 	    	   
 	    	   if og == &quot;OG&quot; 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 &quot;all&quot;    -- round =  all(empty)|group|playoffs
     local group = g.args['group'] or &quot;all&quot;     -- 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 == &quot;group&quot; then validateGroupRound = true end      -- there is a group round
     end
     if validateRound == false and round ~= &quot;all&quot; then 
     	local message = 'Invalid round &quot;' .. round .. '&quot; 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 = &quot;all&quot; 
       - allows group column to be omitted from player table when group table provided 
 ]]
 function p.getGoalsCol(round)
    
     local minimum = 1000
     if round == &quot;all&quot; then  -- if all need column of first round
        for k,v in pairs(data.rounds) do
        	  if v &lt; 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 &quot;no group found&quot;
     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, &quot;&quot;                 -- return number of goals, empty string
 	else
 		p.errorString = p.errorString .. &quot; Invalid goals entry for player &quot; .. player
 		return 0, &quot;&quot;
 	end
 end
 function p.parseComment(comment)
 	
 	local frame = mw.getCurrentFrame()
 
 	-- we have something like &quot;{{efn-ua|name=goals}}&quot;
 	if string.find(comment, &quot;efn&quot; , 1 , true ) then       -- if we have a comment with a note
 		g.notes = true                                    -- set flag
 	end
 	
 	
 	return frame: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] &gt;1 then 
 				p.errorString = p.errorString  .. &quot;\n\nWe have u[1]=&quot; .. u[1] .. &quot; and u[2]=&quot; .. u[2]
 			end]]
 			return u[1], u[2]            -- return player name, player sorting alias
 		else
 			p.errorString = p.errorString .. &quot; Invalid name entry for player &quot; .. u[1] .. &quot;, &quot; .. u[2] 
 			return &quot;&quot;, &quot;&quot;     --TODO errroer
 		end
 	elseif type(u) == 'string' then
 		return u, &quot;&quot;                 -- return player name
 	else
 		p.errorString = p.errorString .. &quot; Invalid name entry for player &quot; .. u or u[1] or &quot;unknown&quot;
 		return &quot;&quot;, &quot;&quot;
 	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, &quot;%[%[&quot;, &quot;&quot;)              -- strip off [[ and ]]
 	name = string.gsub(name, &quot;%]%]&quot;, &quot;&quot;)
     --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
 		                   { &quot;ı&quot;, &quot;i&quot; } , { &quot;İ&quot;, &quot;i&quot; } , { &quot;ß&quot;, &quot;ss&quot; },
 		                   { &quot;ý&quot;, &quot;y&quot; } , { &quot;ř&quot;, &quot;r&quot; } , { &quot;ő&quot;, &quot;o&quot; },
 		                   { &quot;é&quot;, &quot;e&quot; } , { &quot;è&quot;, &quot;e&quot; } , { &quot;þ&quot;, &quot;th&quot; },
 		                   { &quot;ē&quot;, &quot;e&quot; } , { &quot;ņ&quot;, &quot;n&quot; } , { &quot;č&quot;, &quot;c&quot; },
 		                   { &quot;ū&quot;, &quot;u&quot; } , { &quot;ž&quot;, &quot;z&quot; } , { &quot;æ&quot;, &quot;ae&quot; },
 		                   { &quot;å&quot;, &quot;a&quot; } , { &quot;ø&quot;, &quot;o&quot; } , { &quot;ą&quot;, &quot;a&quot; },
 		                   { &quot;ń&quot;, &quot;n&quot; } , { &quot;ł&quot;, &quot;l&quot; } , { &quot;ã&quot;, &quot;a&quot; },
 		                   { &quot;ș&quot;, &quot;s&quot; } , { &quot;š&quot;, &quot;s&quot; } , { &quot;í&quot;, &quot;i&quot; },
 		                   { &quot;á&quot;, &quot;a&quot; } , { &quot;ä&quot;, &quot;a&quot; } , { &quot;ć&quot;, &quot;c&quot; },
 		                   { &quot;ç&quot;, &quot;c&quot; } , { &quot;ğ&quot;, &quot;g&quot; } , { &quot;ö&quot;, &quot;o&quot; },
 		                   { &quot;ë&quot;, &quot;e&quot; } , { &quot;ú&quot;, &quot;u&quot; } , { &quot;ó&quot;, &quot;o&quot; },
 		                   { &quot;ð&quot;, &quot;d&quot; } , { &quot;ü&quot;, &quot;u&quot; } , { &quot;ű&quot;, &quot;u&quot; },
 		                   { &quot;ā&quot;, &quot;a&quot; } , { &quot;ī&quot;, &quot;i&quot; } , { &quot;đ&quot;, &quot;d&quot; },
 		                   { &quot;ă&quot;, &quot;a&quot; } , { &quot;â&quot;, &quot;a&quot; } , { &quot;ż&quot;, &quot;z&quot; },
 		                   { &quot;ț&quot;, &quot;t&quot; } , { &quot;ş&quot;, &quot;s&quot; } , { &quot;ś&quot;, &quot;s&quot; },
 		                   { &quot;ǎ&quot;, &quot;a&quot; } , { &quot;ě&quot;, &quot;e&quot; } , { &quot;ů&quot;, &quot;u&quot; },
 		                   { &quot;ĕ&quot;, &quot;e&quot; } , { &quot;ñ&quot;, &quot;n&quot; } , { &quot;ď&quot;, &quot;d&quot; },
 		                   { &quot;ï&quot;, &quot;i&quot; } , { &quot;ź&quot;, &quot;z&quot; } , { &quot;ô&quot;, &quot;o&quot; },
 		                   { &quot;ė&quot;, &quot;e&quot; } , { &quot;ľ&quot;, &quot;l&quot; } , { &quot;ģ&quot;, &quot;g&quot; },
 		                   { &quot;ļ&quot;, &quot;l&quot; } , { &quot;ę&quot;, &quot;e&quot; } , { &quot;ň&quot;, &quot;n&quot; },
 		                   { &quot;ò&quot;, &quot;o&quot; }
                          }
     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 ~= &quot;&quot; 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 = { &quot;CAM&quot;, &quot;CHN&quot;, &quot;TPE&quot;, &quot;MYA&quot;, &quot;PRK&quot;, &quot;KOR&quot;, &quot;VIE&quot; }
 	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, &quot;|&quot;) then                 -- test for redirect
       	local names = mw.text.split( playerName, &quot;|&quot;)    
        	playerName = names[2]                               -- get name after pipe
     end
 
     local names = mw.text.split( playerName, &quot; &quot;) -- 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 &lt;= #names do                       -- any addition names e.g. Bloggen
 			name= name .. names[i]
 			i=i+1
 		end
 		name = name .. &quot;, &quot; .. 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, &quot;,&quot;)         -- 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&lt;b end)         -- sort the table alphbetically
 
 	local list = &quot;against &quot;                    -- construct the alphabetical list string
 	for i=1, #items do
 		local sep =  &quot;, &quot;                              -- separator for comma-delimited list
 		if i==1 then sep = &quot;&quot;                          -- first word doesn't need comma
 		elseif i==#items then sep = &quot; &amp; &quot;            -- use &quot;and&quot; 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 &gt; b.goals) then                -- primary sort on 'goals' -&gt; a before b
 		        return true
 		    elseif (a.goals &lt; b.goals) then            -- primary sort on 'goals' -&gt; b before a
 		        return false
 		    else -- a.goals == b.goals 		           -- primary sort tied, 
 		        
 		        --return a.country &lt; 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 &lt; country_b) then        -- secondary sort on 'country'
 			        return true
 			    elseif (country_a &gt; country_b) then    -- secondary sort on 'country'
 			        return false
 			    else -- a.country == b.country 		   -- secondary sort tied, 
 
 			        --return a.player &lt; 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 &lt; 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, &quot;|&quot;) then                 -- test for redirect
                     	local names = mw.text.split( a.player, &quot;|&quot;)    
                     	test_a = names[2]                               -- get name after pipe
                     end
                     if string.find (b.player, &quot;|&quot;) then
                     	local names = mw.text.split( b.player, &quot;|&quot;)
                     	test_b = names[2]
                     end
                     
 			        local names_a = mw.text.split( test_a, &quot; &quot;) -- we don't want to sort on first name
 			        local names_b = mw.text.split( test_b, &quot; &quot;) --     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] &lt; names_b[2]      -- sort on second name
 ]]			        
 			    end		        
 		    end
 		end
 		
     table.sort(g.goalscorers, sort_function)
 
 
 end
 function   p.tabulateGoalscorers(frame, 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 = &quot;&quot;
     local firstplayerCell = &quot;&quot;
 
     local tableString = '\n{| class=&quot;wikitable&quot;'                                           -- 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 == &quot;complete&quot; then playerActive = false end  -- overrides active_countries
     	
     	-- wikitext for tablulated list    
        	local goalscorerString = p.addLinkedIcon(frame, u['country'])    -- linked flag icon    
     	if playerActive and g.args['bold']~='no' then
     		goalscorerString = goalscorerString  .. &quot; '''&quot; .. u['player'] .. &quot;'''&gt;&quot;    -- bolded name
     	else
    		    goalscorerString = goalscorerString  .. &quot; &quot; .. 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'] &lt; 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 &gt; maxRank * 1.5 then
 		        	firstplayerCell = '\n| style=&quot;font-style:italic;text-align:center;&quot;|' .. rankCount .. &quot; players&quot;
 		        	playerCells = &quot;&quot;
 		        	rowSpan = 1
 		        end
 		        tableString = tableString .. '\n|-\n| style=&quot;text-align:center;&quot; rowspan=&quot;' .. rowSpan .. '&quot;|' .. rank 
 		        --if rankCount &gt; 1 then tableString = tableString  .. &quot;=&quot; end  -- adds equals when rank shared
 		        tableString = tableString .. firstplayerCell 
 		        tableString = tableString .. '\n| style=&quot;text-align:center;&quot; rowspan=&quot;' .. rowSpan .. '&quot;|'  ..  goalNumber
 		        tableString = tableString .. playerCells
 
 		        rank = rank + rankCount	
 		        if rank &gt; 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 = &quot;&quot;
 			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 ~= &quot;&quot; then
         tableString = tableString .. &quot;\n|}&quot;
 		return  tableString
 	else
 		return (&quot;No goals matching requested criteria.&quot;)
 	end
 	
 	
 end
 function   p.outputGoalscorers(frame, og) -- output list of goalscorers
     if g.args['table'] then return p.tabulateGoalscorers(frame, og) end  -- optional table output
     
     local outputString = &quot;&quot;	
     if og == &quot;OG&quot; 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 {{Div Col}} 
     
     for j,u in pairs(g.goalscorers) do    -- run through sorted list of selected goalscorers
     	
     	--if u['goals'] &lt; goalMinimum then break end -- limit list to goals over a threshold (now handled in select goalscorers)
     		
     	if u['goals'] &lt; goalNumber then         -- start new list of new number of goals
     		if listOpen then                    -- if an open list, close last list
     			outputString = outputString .. p.closeList(frame) 
     			listOpen = false -- redundant as will be set true again
     		end
     		goalNumber = u['goals']
     		
     		local goalString = &quot; கோல்&quot;
     		--if og == &quot;OG&quot; then 	
     		if goalNumber &lt; 0 then
     			goalString = &quot; சுய&quot; .. goalString 
     		end
     		if  math.abs(u['goals']) ~= 1 then goalString = goalString .. &quot;s&quot; end
 
     		outputString = outputString .. &quot;\n'''&quot; .. math.abs(u['goals']) .. goalString .. &quot;'''&quot;   -- list caption
     		
     		outputString = outputString .. p.openList(frame,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 == &quot;complete&quot; then playerActive = false end  -- overrides active_countries
     	
     	-- wikitext for bullet list    
        	local goalscorerString = '\n*&lt;span&gt;' .. p.addLinkedIcon(frame, u['country'])    -- linked flag icon    
     	if playerActive and g.args['bold']~='no' then
     		goalscorerString = goalscorerString  .. &quot; &lt;b&gt;&quot; .. u['player'] .. &quot;&lt;/b&gt;&quot;    -- bolded name
     	else
    		    goalscorerString = goalscorerString  .. &quot; &quot; .. u['player']                  -- name
     	end
     	goalscorerString = goalscorerString  .. u['comment']   .. '&lt;/span&gt;'             -- comment for o.g.
     	                              
     	outputString = outputString .. goalscorerString   --  .. &quot; &quot; .. tostring(u['goals'])
 
     end -- reached end of list of goalscorers
 
 	if outputString ~= &quot;&quot; then
 	    outputString = outputString .. p.closeList(frame)
 
 		return outputString
 	else
 		return (&quot;No goals matching requested criteria.&quot;)
 	end
 end
 
 -- output icon linked to national team page
 function p.addLinkedIcon(frame, country)
 	local icon = data.templates['flag_icon_linked']         -- fbicon etc set in data module
 	local level = data.templates['youth_level']  or &quot;&quot;           -- parameter for youth level, ie under-21
     -- equivalent to  {{fbicon|country}}     
     local flagVariant = &quot;&quot;
     if data.templates.flagvar and data.templates.flagvar[country] then
     	flagVariant = data.templates.flagvar[country] 
     end
     if level ~= &quot;&quot; then 
     	return frame:expandTemplate{ title = icon , args = { level, country, flagVariant } }   
     else
     	return frame:expandTemplate{ title = icon , args = { country, flagVariant } }     -- flag icon
     end
 end
 -- formatting of list under each number of goals
 function p.openList(frame,og)
 
 	return mw.getCurrentFrame():extensionTag{
 		name = 'templatestyles', args = { src = 'Div col/styles.css' }
 	} .. '&lt;div class=&quot;div-col&quot; style=&quot;column-width:25em;&quot;&gt;' -- perhaps add &quot;column-count:3;&quot;&quot; to limit max number of columns?
 end
 function p.closeList(frame)
    return '&lt;/div&gt;'
 end
 function p.firstToUpper(str)
     return (str:gsub(&quot;^%l&quot;, 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 .. &quot; Players highlighted in '''bold''' are still active in the competition.&quot;
     end
     if g.args['further'] then
     	if text ~= &quot;&quot; then text = text .. &quot; &quot; end
     	text = text .. g.args['further']
     end
     if g.args['extra'] then
     	text = text .. &quot;\n\n&quot; .. 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, &quot;\n&quot;, 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( &quot;Entry: &quot; .. list  .. &quot;[&quot; .. count .. &quot;]&quot;)
 
 	return totalGoals 
 end
 
 --[[ use data supplied by template 
 ]]
 
 --function p.list(frame)
 function p.useTemplateData(frame)
     --getArgs(frame)
     
     --[[ {{{#if:{{{assists|}}}||There 
                   {{#if:{{{ongoing|}}}|{{#ifexpr:{{{goals}}}=1|has|have}} been
                   |{{#ifexpr:{{{goals}}}=1|was|were}}}} {{{goals}}} 
                   {{#ifexpr:{{{goals}}}=1|goal|goals}} scored{{#if:{{{players|}}}|&amp;nbsp;by {{{players}}} 
                   {{#ifexpr:{{{players}}}=1|player|different players}}
                   {{#if:{{{own goals|}}}|&amp;nbsp;(with {{{own goals}}} of them credited as {{#ifexpr:{{{own goals}}}=1|an own goal|own goals}})|}}|}} in {{{matches}}} 
                   {{#ifexpr:{{{matches}}}=1|match|matches}}, for an average of {{#expr:{{{goals}}}/{{{matches}}} round 2}} 
                   {{#ifexpr:({{{goals}}}/{{{matches}}} round 2)=1|goal|goals}} per match
                   {{#if:{{{updated|}}}|&amp;nbsp;(as of {{{updated}}})}}.}}{{#if:{{{bold|}}}|{{#if:{{{assists|}}}||&amp;nbsp;}}
                   Players highlighted in '''bold''' are still active in the competition.
                   |}}{{#if:{{{further|}}}|{{#if:{{{assists|}}}||&amp;nbsp;}}{{{further}}}|}}
                   {{#if:{{{extra|}}}|{{{extra}}}{{clear}}|}}
     --]]
     local statNumber = g.args['goals'] or g.args['assists'] or 0
     local matches = g.args['matches']
     local statType = &quot;goal&quot;
     if g.args['assists'] then statType = &quot;assist&quot; end
     if g.args['clean sheets'] then statType = &quot;clean sheet&quot; end
     local ongoing = g.args['ongoing']
     local text1 = &quot;There&quot;
     if g.args['lc'] then text1 = &quot;there&quot; end  
     local text2 = &quot;were&quot;
     if ongoing then text2 = &quot;have been&quot; end  
     local updateString = &quot;&quot;
     local averageString = &quot;&quot;
     local goalPlural = &quot;s&quot;                                                                    -- goal(s)
     if g.args['goals'] and tonumber(g.args['goals']) == 1 then 
     	goalPlural = &quot;&quot;
     	text2 = &quot;was&quot;
     	if ongoing then text2 = &quot;has been&quot; end  
     end
     local matchPlural = &quot;es&quot;                                                                     -- match(es)
     if g.args['matches'] and tonumber(g.args['matches']) == 1 then matchPlural = &quot;&quot;   end
     	
     	
     -- auto version: string.format(&quot; in %d match%s, for an average of %.&quot;..precision..&quot;g goal%s per match&quot;, matches, pluralMatches, average, pluralAverage)
     if g.args['goals'] and g.args['matches'] then
     	local averageGoals = g.args['goals']/g.args['matches']
     	local avGoalPlural = &quot;s&quot;
     	if averageGoals == 1 then avGoalPlural = &quot;&quot;  end
     	averageString = string.format(&quot; in %d match%s, for an average of %.3g goal%s per match&quot;, g.args['matches'], matchPlural, averageGoals, avGoalPlural)
     end    
     if g.args['updated'] and g.args['updated'] ~= &quot;complete&quot; then
     	updateString = &quot;&amp;nbsp;(as of &quot; ..g.args['updated'] .. &quot;)&quot;
     end
     local sep = &quot;.&quot;
     if g.args['sep'] then sep = g.args['sep'] end
     local text = &quot;&quot;
     if g.args['goals'] then
     	text = string.format(&quot;%s %s %d %s%s scored%s&quot;, 
     	                     text1, text2, statNumber, statType, goalPlural, averageString..updateString..sep)
     end
     text = p.addAdditionHeaderText(text)  -- handles template parameters bold, further, extra
     
     --[[   {{#if:{{{30 goals|{{{30 assists|}}}}}}|'''30 {{#if:{{{assists|}}}|assists|goals}}'''
                  &lt;div class=&quot;div-col columns column-count column-count-3&quot; style=&quot;column-count:3;&quot;&gt;
                  {{#if:{{{assists|}}}|{{{30 assists}}}|{{{30 goals}}}}}&lt;/div&gt;|}}]]
     local output = &quot;\n&quot;
     local number = 30
    
     local totalGoals = 0
     
     while number &gt; -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 &lt; 0 then  
        	  entry = g.args[math.abs(number) .. ' own goals'] or g.args[math.abs(number) .. ' own goal']
        	  statType = &quot;own goal&quot;
        end
        local plural = &quot;s&quot;
        if number == 1 or number == -1 then plural = &quot;&quot; end
 			
        if entry then                                    -- do we have goals/assists for this number
 
     	 output = output .. &quot;\n'''&quot; .. tostring(math.abs(number)) .. &quot; &quot; .. statType .. plural .. &quot;'''\n&quot; 
     	                 .. p.openList(frame) .. &quot;\n&quot; .. entry .. p.closeList(frame)
     	 totalGoals = p.countGoals(entry, number, totalGoals)
        end
        
        number = number -1
     end
     
     if statType == &quot;goal&quot; or statType == &quot;own goal&quot; then
     	if g.args['goals'] and totalGoals ~= tonumber(g.args['goals']) then 
     	    mw.addWarning(&quot;WARNING. Mismatch between number of goals listed (&quot; .. totalGoals .. &quot;) and goals parameter (&quot; .. g.args['goals']  .. &quot;).&quot;)
     	end
     end
     
     --{{#if:{{{bottom|}}}|{{small|{{{bottom_text}}}}} &lt;div class=&quot;div-col columns column-count column-count-3&quot; style=&quot;column-count:3;&quot;&gt; {{{bottom}}}&lt;/div&gt;|}}{{#if:{{{source|}}}|{{smaller|Source: {{{source}}}}}|}}
     local footerText = g.args['footer-text']  or g.args['bottom'] or &quot;&quot;
     local footerHeading = g.args['footer-heading'] or  g.args['bottom-text'] or &quot;&quot;
     local footer = &quot;&quot;
     if footerText ~= &quot;&quot; then
     	local heading = &quot;&quot;
     	if footerHeading ~= &quot;&quot; then
     		heading = '&lt;p&gt;' .. footerHeading .. '&lt;/p&gt;'
     	end
     	footer =  '\n' ..  heading  .. p.openList(frame)  .. '\n' .. footerText .. p.closeList(frame)
     end
     
     
     --{{#if:{{{source|}}}|{{small|Source: {{{source}}}}}|}}
     local source = g.args['source'] or &quot;&quot;
     if source ~= &quot;&quot; then source = &quot;&lt;small&gt;Source: &quot; .. source .. &quot;&lt;/small&gt;&quot; end
     
     return text .. output .. footer .. source
 end