# Module:Template invocation

-- This module provides functions for making MediaWiki template invocations.  

local checkType = require('libraryUtil').checkType  

local p = {}  

---
-- Name: p.name  
-- Purpose: Find a template invocation name from a page name or a  
-- mw.title object.  
-- Description: This function detects whether a string or a mw.title  
-- object has been passed in, and uses that to find a  
-- template name as it is used in template invocations.  
-- Parameters: title - full page name or mw.title object for the  
-- template (string or mw.title object)  
-- Returns: String  
---

function p.name(title)  
if type(title) == 'string' then  
title = mw.title.new(title)  
if not title then  
error("invalid title in parameter #1 of function 'name'", 2)  
end  
elseif type(title) ~= 'table' or type(title.getContent) ~= 'function' then  
error("parameter #1 of function 'name' must be a string or a mw.title object", 2)  
end  
if title.namespace == 10 then  
return title.text  
elseif title.namespace == 0 then  
return ':' .. title.prefixedText  
else  
return title.prefixedText  
end  
end  

---
-- Name: p.invocation  
-- Purpose: Construct a MediaWiki template invocation.  
-- Description: This function makes a template invocation from the  
-- name and the arguments given. Note that it isn't  
-- perfect: we have no way of knowing what whitespace was  
-- in the original invocation, the named parameters will be  
-- alphabetically sorted, and any parameters with duplicate keys  
-- will be removed.  
-- Parameters: name - the template name, formatted as it will appear  
-- in the invocation. (string)  
-- args - a table of template arguments. (table)  
-- format - formatting options. (string, optional)  
-- Set to "nowiki" to escape, curly braces, pipes and  
-- equals signs with their HTML entities. The default  
-- is unescaped.  
-- Returns: String  
---

function p.invocation(name, args, format)  
checkType('invocation', 1, name, 'string')  
checkType('invocation', 2, args, 'table')  
checkType('invocation', 3, format, 'string', true)  

-- Validate the args table and make a copy to work from. We need to  
-- make a copy of the table rather than just using the original, as  
-- some of the values may be erased when building the invocation.  
local invArgs = {}  
for k, v in pairs(args) do  
local typek = type(k)  
local typev = type(v)  
if typek ~= 'string' and typek ~= 'number'  
or typev ~= 'string' and typev ~= 'number'  
then  
error("invalid arguments table in parameter #2 of " ..  
"'invocation' (keys and values must be strings or numbers)", 2)  
end  
invArgs[k] = v  
end  

-- Get the separators to use.  
local seps = {  
openb = '',  
pipe = '|',  
equals = '='  
}  
if format == 'nowiki' then  
for k, v in pairs(seps) do  
seps[k] = mw.text.nowiki(v)  
end  
end  

-- Build the invocation body with numbered args first, then named.  
local ret = {}  
ret[#ret + 1] = seps.openb  
ret[#ret + 1] = name  
for k, v in ipairs(invArgs) do  
if type(v) == 'string' and v:find('=', 1, true) then  
-- Likely something like 1=foo=bar which needs to be displayed as a named arg.  
else  
ret[#ret + 1] = seps.pipe  
ret[#ret + 1] = v  
invArgs[k] = nil -- Erase the key so that we don't add the value twice  
end  
end  
local keys = {} -- sort parameter list; better than arbitrary order  
for k, _ in pairs(invArgs) do  
keys[#keys + 1] = k  
end  
table.sort(keys, function (a, b)  
-- Sort with keys of type number first, then string.  
if type(a) == type(b) then  
return a < b  
elseif type(a) == 'number' then  
return true  
end  
end)  
for _, v in ipairs(keys) do -- Add named args based on sorted parameter list  
ret[#ret + 1] = seps.pipe  
ret[#ret + 1] = tostring(v)  
ret[#ret + 1] = seps.equals  
ret[#ret + 1] = invArgs[v]  
end  
ret[#ret + 1] = seps.closeb  

return table.concat(ret)  
end  

return p