Module:Template wrapper

require('strict');local error_msg = '<span style=\"font-size:100%\" class=\"error\"><code style=\"color:inherit; border:inherit; padding:inherit;\">&#124;_template=</code> missing or empty</span>';--[[--------------------------< I S _ I N _ T A B L E >--------------------------------------------------------scan through tbl looking for value; return true if found, false else]]local function is_in_table (tbl, value)    for k, v in pairs (tbl) do        if v == value then return true end    end    return false;end--[[--------------------------< A D D _ P A R A M E T E R >----------------------------------------------------adds parameter name and its value to args table according to the state of boolean list argument; kv pair fortemplate execution; k=v string for template listing.]]local function add_parameter (k, v, args, list)if list thentable.insert( args, table.concat ({k, '=', v}));-- write parameter names and values to args table as stringelseargs[k] = v;-- copy parameters to args tableendend--[[--------------------------< A L I A S _ M A P _ G E T >----------------------------------------------------returns a table of local template (parent frame) parameter names and the target template names that match wherein [key]=<value> pairs where:[key] is local template parameter name (an alias)<value> is target template parameter name (the canonical parameter name used in the working template)The parameter |_alias-map= has the form:|_alias-map=<list>where <list> is a comma-separated list of alias / canonical parameter name pairs in the form<from> : <to>where:<from> is the local template's parameter name (alias)<to> is the target template's parameter name (canonical)for enumerated parameters place an octothorp (#) where the enumerator digits are placed in the parameter names:<from#> : <to#>]]local function alias_map_get (_alias_map)local T = mw.text.split (_alias_map, '%s*,%s*');-- convert the comma-separated list into a table of alias pairslocal mapped_aliases = {};-- mapped aliases will go herelocal l_name, t_name;-- parameter namesfor _, alias_pair in ipairs (T) do-- loop through the table of alias pairsl_name, t_name = alias_pair:match ('(.-)%s*:%s*(.+)');-- from each pair, get local and target parameter namesif l_name and t_name then-- if both are setif tonumber (l_name) thenl_name = tonumber (l_name);-- convert number-as-text to a numberendmapped_aliases[l_name] = t_name;-- add them to the map tableendendreturn mapped_aliases;end--[[--------------------------< F R A M E _ A R G S _ G E T >--------------------------------------------------Fetch the wrapper template's 'default' and control parameters; adds default parameters to argsreturns content of |_template= parameter (name of the working template); nil else]]local function frame_args_get (frame_args, args, list)local template;for k, v in pairs (frame_args) do-- here we get the wrapper template's 'default' parametersif 'string' == type (k) and (v and ('' ~= v)) then-- do not pass along positional or empty parametersif '_template' == k thentemplate = v;-- save the name of template that we are wrappingelseif '_exclude' ~= k and '_reuse' ~= k and '_include-positional' ~= k  and '_alias-map' ~= k then-- these already handled so ignore here; add_parameter (k, v, args, list);-- add all other parameters to args in the style dictated by listendendendreturn template;-- return contents of |_template= parameterend--[=[--------------------------< P F R A M E _ A R G S _ G E T >------------------------------------------------Fetches the wrapper template's 'live' parameters; adds live parameters that aren't members of the exclude table toargs table; positional parameters may not be excludedno return value]=]local function pframe_args_get (pframe_args, args, exclude, _include_positional, list)for k, v in pairs (pframe_args) doif 'string' == type (k) and not is_in_table (exclude, k) then-- do not pass along excluded parametersif v and ('' ~= v) then-- pass along only those parameters that have assigned valuesif 'unset' == v:lower() then-- special keyword to unset 'default' parameters set in the wrapper templatev = '';-- unset the value in the args tableendadd_parameter (k, v, args, list)-- add all other parameters to args in the style dictated by list; alias map only supported for local-template parametersendendendif _include_positional thenfor i, v in ipairs (pframe_args) do-- pass along positional parametersif 'unset' == v:lower() then-- special keyword to unset 'default' parameters set in the wrapper templatev = '';-- unset the value in the args tableendadd_parameter (i, v, args, list);endendend--[[--------------------------< _ M A I N >--------------------------------------------------------------------Collect the various default and live parameters into args styled according to boolean list.returns name of the working or listed template or nil for an error message]]local function _main (frame, args, list)local template;local exclude = {};-- table of parameter names for parameters that are not passed to the working templatelocal reuse_list = {};-- table of pframe parameter names whose values are modified before they are passed to the working template as the same namelocal alias_map = {};-- table that maps parameter aliases to working template canonical parameter nameslocal _include_positional;if frame.args._exclude and ('' ~= frame.args._exclude) then-- if there is |_exclude= and it's not emptyexclude = mw.text.split (frame.args._exclude, "%s*,%s*");-- make a table from its contentsend-- TODO: |_reuse= needs a better name (|_reuse=)if frame.args._reuse and ('' ~= frame.args._reuse) then-- if there is |_reuse= and it's not emptyreuse_list = mw.text.split (frame.args._reuse, "%s*,%s*");-- make a table from its contentsendif frame.args['_alias-map'] and ('' ~= frame.args['_alias-map']) then-- if there is |_alias-map= and it's not emptyalias_map = alias_map_get (frame.args['_alias-map']);-- make a table from its contentsendtemplate = frame_args_get (frame.args, args, list);-- get parameters provided in the {{#invoke:template wrapper|...|...}}if nil == template or '' == template then-- this is the one parameter that is required by this modulereturn nil;-- not present, tell calling function to emit an error messageend_include_positional = 'yes' == frame.args['_include-positional'];-- when true pass all positional parameters along with non-excluded named parameters to ...-- ... the working template; positional parameters are not excludablelocal _pframe_args = frame:getParent().args;-- here we get the wrapper template's 'live' parameters from pframe.argslocal pframe_args = {};-- a local table that we can modifyfor k, v in pairs (_pframe_args) do-- make a copy that we can modifypframe_args[k] = v;end-- here we look for pframe parameters that are aliases of canonical parameter names; when found-- we replace the alias with the canonical.  We do this here because the reuse_list works on-- canonical parameter names so first we convert alias parameter names to canonical names and then-- we remove those canonical names from the pframe table that are reused (provided to the working-- template through the frame args table)for k, v in pairs (alias_map) do-- k is alias name, v is canonical nameif pframe_args[k] then-- if pframe_args has parameter with alias namepframe_args[v] = _pframe_args[k];-- create new canonical name with alias' valuepframe_args[k] = nil;-- unset the aliasendendfor k, v in pairs (pframe_args) do-- do enumerated parameter alias -> canonical translationif 'string' == type (k) then-- only named parameters can be enumeratedif alias_map[k..'#'] then-- non-enumerated alias matches enumerated parameter pattern? enumerator at end onlypframe_args[alias_map[k..'#']:gsub('#', '')] = v;-- remove '#' and copy parameter to pframe_args tablepframe_args[k] = nil;-- unset the aliaselseif k:match ('%d+') then-- if this parameter name contains digitslocal temp = k:gsub ('%d+', '#');-- make a copy; digits replaced with single '#'local enum = k:match ('%d+');-- get the enumeratorif alias_map[temp] then-- if this parameter is a recognized enumerated aliaspframe_args[alias_map[temp]:gsub('#', enum)] = v;-- use canonical name and replace '#' with enumerator and add to pframe_argspframe_args[k] = nil;-- unset the aliasendendendend-- pframe parameters that are _reused are 'reused' have the form something like this:--|chapter=[[wikisource:{{{chapter}}}|{{{chapter}}}]]-- where a parameter in the wrapping template is modified and then passed to the working template-- using the same parameter name (in this example |chapter=)-- remove parameters that will be reusedfor k, v in ipairs (reuse_list) do-- k is numerical index, v is canonical parameter name to ignoreif pframe_args[v] then-- if pframe_args has parameter that should be ignoredpframe_args[v] = nil;-- unset the ignored parameterendendpframe_args_get (pframe_args, args, exclude, _include_positional, list);-- add parameters and values to args that are not listed in the exclude tablereturn template;-- args now has all default and live parameters, return working template nameend--[[--------------------------< W R A P >----------------------------------------------------------------------Template entry point.  Call this function to 'execute' the working template]]local function wrap (frame)local args = {};-- table of default and live parameters and their values to be passed to the wrapped templatelocal template;-- the name of the working templatetemplate = _main (frame, args, false);-- get default and live parameters and the name of the working templateif not template then-- template name is requiredreturn error_msg;-- emit error message and abandon if template name not presentendreturn frame:expandTemplate {title=template, args=args};-- render the working templateend--[[--------------------------< L I S T >----------------------------------------------------------------------Template entry point.  Call this function to 'display' the source for the working template.  This function addedas a result of a TfD here: Wikipedia:Templates_for_discussion/Log/2018_April_28#Module:PassArgumentsThis function replaces a similarly named function which was used in {{cite compare}} and {{cite compare2}}Values in the args table are numerically indexed strings in the form 'name=value']]local function list(frame, do_link)local args = {};-- table of default and live parameters and their values to be passed to the listed templatelocal template;-- the name of the listed templatetemplate = _main (frame, args, true);-- get default and live parameters and the name of the listed templateif not template then-- template name is requiredreturn error_msg;-- emit error message and abandon if template name not presentendif do_link thentemplate = ('[[%s|%s]]'):format(frame:expandTemplate{ title='Transclude', args = {template} }, template)endtable.sort(args)for i = 1, #args dolocal stripped = args[i]:match('^' .. i .. '=([^=]*)$')if stripped then args[i] = stripped else break endendreturn frame:preprocess(table.concat({'<code style="color:inherit; background:inherit; border:none;">&#123;&#123;',template,('<wbr><nowiki>|%s</nowiki>'):rep(#args):format(unpack(args)), '&#125;&#125;</code>'}));-- render the templateendlocal function link (frame)return list(frame, true)end--[[--------------------------< E X P O R T E D   F U N C T I O N S >------------------------------------------]]return {link = link,list = list,wrap = wrap,};