Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

0 The module uses frequently the template {{=}} for title = "=" 0

This module contains the functions for different templates, solving their iteration problem

The MediaWiki template coding does not support the repetitive processing of a parameter set, which leads often to a very primitive and long chain of parameter checks in the kind of:

{{#if:{{{1|}}} | perform an action with parameter 1 }}
{{#if:{{{2|}}} | perform an action with parameter 2 }}
{{#if:{{{3|}}} | perform an action with parameter 3 }}
{{#if:{{{4|}}} | perform an action with parameter 4 }}
•••
•••
{{#if:{{{33|}}} perform an action with parameter 33 }}

with the disadvantage that such a construct will fail as soon as there comes a 34nd parm. I always thought that with Lua and its for loops this iterations should be possible, somehow. After long searching without any success and without an idea how to perform it, I asked at several forums and got finally from User:Trappist the monk the helping hint to solve it with a control structure like:

	local out = {}
	for i, v in ipairs (args) do
		table.insert (out, frame:expandTemplate{ title = title, args = v })
	end
	return table.concat (out)

The functions contained therein are from very simple, just one parameter to pass, to more complicated ones, where e.g. a pair of parameters needs kind of a flip-flop switch when always a tuple of e.g. an item and its correlated text needs to be passed.

When there are tuples, triples etc., the table.insert occurs, when the last element is processed; this performs problems when the last group is incomplete – it should be finished with empty values to trigger the table.insert. The check with table.maxn seems not to work correctly?

This problem is solved differently, for the function attribs with a primitive workaround.

Namespace lists edit

There are special lists for some namespaces:

  • Filelist - for items of the namespace 6
  • Userlist - for items of the namespace 2
  • Linklist - for items of the namespace 000 (not yet)
  • Templist - for items of the namespace 100 (not yet)
  • Cat-list - for items of the namespace 140 (not yet)
  • Mod-list - for items of the namespace 828 (not yet)

Filelist edit

File list templates edit

The three templates {{Ifim}}, {{Ifim1}} and {{Ifimt}} avoid to display the file itself as an |other version=.

There are currently five templates creating file lists with the function:filelist

  1. {{Other versions}} a vertical list (without initial)
  2. {{Filelist}} a horizontal list (without initial)
  3. {{Derivative versions}} a vertical list
  4. {{Derived from}} a vertical list
  5. {{File}} a horizontal list

All these templates are autoreferencing, and the support also the |gallery= option.

Other file lists edit

The function:svglang creates a list of just file_names and language_codes, for:

  1. {{Svg lang}}
  2. {{Lang gallery}}
  3. {{Multilingual SVG diagrams}}

Much more is created by the function:ownbased for the template:

  1. {{Own based}}

which will handle in the majority of cases just one file, but can serve an unlimited number.

This list can be controlled by the user to be displayed either horizontally or vertically.

  1. {{Attribs}}

The template works, whether there is a user assigned for a file, or one filename follows another.

  1. {{Emoji}}

The template displays a line of SVG emojis; Unicode characters/emojis can be displayed with the function emodis or emotab.

Variable iterations edit

A very simple iteration, usable for everything, is performed by function:iterate

used by e.g. {{Image extracted}}, {{Image translated}},

More usable is the function:iteration for transclusions of a template with:

  • a number of positional parameters which can change at each transclusion, and
  • a number of positional parameters which are the same for all transclusions.

See template {{Iterate}}. The function:loop is used by {{Repeat}}.

samefile function edit

When the name of one of the files is the same as the first one, it is enough to reference it with its extension after a dot, e.g. just ".jpeg". The most standard extensions can be abbreviated, e.g. ".j" will be sufficient for ".jpg" (only for lower case!).

The function incrhx increments a hexadecimal number (parm 1) by a decimal number (parm 2).

Valid numbers are assumed, no checking occurs!

When parameter 2 is preceded by a minus, the first number is decremented by the second one.

Userlist edit

  • Userlist: {{Userlist}} with many formatting parameters

Linklist edit

Templist edit

Other iterations edit

  • Langlist:
  • Colorbox: {{ColorString}} and other color box templates
  • Legends : {{Legtab}} combines single {{Legend}}s
  • Tleparms: the metatemplate {{Tle}} displays the parameter usage of a template
  • Navigate: {{Navcat}} navigates through color categories
  • Showname: {{Showname}} helper template, for parameter tests
  • Emodis 0: {{Emoji}} displays (one or many) Unicode characters
The function emodis displays Unicode characters / emojis; it can care for newlines, while
The function emotab displays a table of files / emojis.

Code

--  This module contains the functions for different templates, solving their iteration problems.


local p = {}

-- for Template:Ifim 
function p.ifim1 (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	local p1 = ppar.p1 or "";
	local p2 = ppar.p2 or "";
	local p3 = ppar.p3 or "";
	local p4 = ppar.p4 or "";
	local p5 = ppar.p5 or "";
	local fn = ppar.fn or "";
	for _, v in ipairs(ppar) do
		table.insert(otab, frame:expandTemplate{ title = "Ifim1", args = { v, p1, p2, p3, p4, p5, n= fn } });
	end
	return  table.concat (otab)
end -- function ifim

-- for Template:Ifimc 
function p.ifim2 (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	local p1 = ppar.p1 or "";
	local p2 = ppar.p2 or "";
	local p3 = ppar.p3 or "";
	local p4 = ppar.p4 or "";
	for _, v in ipairs(ppar) do
		table.insert(otab, frame:expandTemplate{ title = "Ifim2", args = { v, p1, p2, p3, p4, v } });
	end
	return  table.concat (otab)
end -- function ifim2

-- for Template:Ifimt (param pairs)
function p.ifimt (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	local v1 = "";
	local p1 = ppar.p1 or "";
	local p2 = ppar.p2 or "";
	local p3 = ppar.p3 or "";
	local p4 = ppar.p4 or "";
	for _, v in ipairs(ppar) do
		if v1 == "" then   
			v1 = mw.text.trim( v );
		else
			table.insert(otab, frame:expandTemplate{ title = "Ifim2", args = { v1, p1, p2, p3, p4, v } });
			v1 = "";
		end
	end
	if v1 ~= "" then
		table.insert(otab, frame:expandTemplate{ title = "Ifim2", args = { v1, p1, p2, p3, p4, "" } });	-- last elem		
	end
	return  table.concat (otab)
end -- function ifimt

-------------------------------------------------------
-- helper function for:  ownbased, and filelist
local function samefile ( fnam, numb, frst ) 
	local name  = mw.text.trim( fnam ) or ""
	if	  mw.ustring.sub(name, 1, 1) == "."
		then
			local nsnr = tonumber( mw.title.getCurrentTitle().namespace );
			local page = mw.title.getCurrentTitle().text;
			local part = "Example"   --  default name
			if nsnr == 6 or nsnr == 7
			then local parts = mw.text.split(page , '.', true)
				 local upTo = #parts - 1    -- copied from Module:File
				 part = table.concat(parts, '.', 1, math.max(upTo, 1))
			end
			if     name == "."  then name = part .. ".png";
			elseif name == ".p" then name = part .. ".png";
			elseif name == ".j" then name = part .. ".jpg";
			elseif name == ".g" then name = part .. ".gif";
			elseif name == ".s" then name = part .. ".svg";
			elseif name == ".t" then name = part .. ".tif";
			elseif name == ".x" then name = part .. ".xcf";	-- exotics:
			elseif name == ".d" then name = part .. ".djvu";
			elseif name == ".m" then name = part .. ".mid";
			elseif name == ".o" then name = part .. ".ogg";
			elseif name == ".v" then name = part .. ".wav";
			elseif name == ".w" then name = part .. ".webp";
			else					 name = part ..  name;
			end
	elseif  name ==  ""  or	 name == "*"
		then
			local nsnr = tonumber( mw.title.getCurrentTitle().namespace );
			local page = mw.title.getCurrentTitle().text;
			if tonumber( numb ) == 1
					then name = "Example.svg";
					if nsnr == 6 or nsnr == 7
						then name = page;
					end
			else name = mw.text.trim( frst );
				if name == ""  or  name == "*"
				or mw.ustring.sub(name, 1, 1) == "."
					then name = "Example.svg";
					if nsnr == 6 or nsnr == 7
						then name = page;
					end
				end
			end
	end
	return name;
end -- function samefile

---++++++++++++++++++++++++++++++++++++++++++++++++++++

-- simple iterations - without many params 
function p.iterate (frame) 
	local gpar = frame.args			-- global parms
	local titl = gpar [1] or "";	-- template name
	local var1 = gpar [2] or "";	-- positional p1
	local var2 = gpar [3] or "";	-- positional p2
	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	for _, v in ipairs(ppar) do
		table.insert(otab, frame:expandTemplate{ title = titl, args = { mw.text.trim(v), var1, var2 } });
	end
	return  table.concat (otab)
end -- function iterate

-- more iterations - for params, param pairs, or n-tupels 
function p.iteration (frame) 
	local gpar = frame.args			-- global parms
	local titl = "";				-- template name
	local vmax = tonumber(gpar["n"]) or 2;	-- tuple number (default=2)
	local vnum = vmax + 1;			-- additional not-changing params 
	local ptab = {};				-- parmameters
	local otab = {};				-- output
	for i, p in ipairs(gpar) do
		if i == 1 then
			titl = mw.text.trim( p ) or "";
		else
			ptab [vnum] = mw.text.trim( p ) or "";
			vnum = vnum + 1;
		end
	end
	vnum = 1;
	local ppar = mw.getCurrentFrame(): getParent().args;

	for _, v in ipairs(ppar) do
		ptab [vnum] = mw.text.trim( v );
		vnum = vnum + 1;
		if vnum > vmax then   
			table.insert(otab, frame:expandTemplate{ title = titl, args = ptab });
			vnum = 1;
		end
	end
	return  table.concat (otab)
end -- function iteration

-- for different templates, for general use, e.g. Emoji
function p.parlst ( frame )
--	local lpar = frame     --  local parms
	local gpar = frame.args-- global parms
--	local ppar = mw.getCurrentFrame(): getParent().args
	local titl = gpar.temp or ""
	if titl ~= "" then 
		local part = {
			nocat = gpar.nocat,
		}
		for i = 1, 9  do	-- fix 9; can be dynamic 
			local p = gpar[i]
			if p == '' then p = nil end
			part[i] = p
		end

		local cunt = 1								--	default:  go 1×	for nil
		if part[1] ~= nil	then cunt = part[1]	end	--	iteration count 
		local otab = {}
		for i = 1, cunt	do
			if part[1] ~= nil then part[1] = i - 1 end
			if gpar.ff ~= nil and gpar.ff > "" then		-- inbetween ?
				table.insert(otab, frame:expandTemplate { title=gpar.ff, args = { gpar.pf } })
			end
			table.insert(otab, frame:expandTemplate { title=titl, args=part } )
		end
		return table.concat (otab)
	end
end --  function parlst


-- global function for one filename
function p.filename (frame) 
--  local lpar = frame     --  local parm
	local gpar = frame.args-- global parm 
	return samefile ( mw.text.trim (gpar[1] or ""), 1 )
end -- function filename

-- for Template:Own based (one filename which is ".")
function p.ownbasby (frame) 
	local gpar = frame.args-- global parm 
	return table.concat({frame:expandTemplate{title="F",args={samefile(gpar[1]or".",1),by=(mw.text.trim(gpar[2]or""))}}})
end -- function ownbasby
-- for Template:Own based  (horizontal - but vertical when "b1=")
function p.ownbased (frame)
--	local lpar = parm       --  local parms
--	local gpar = frame.args -- global parms
	local ppar = mw.getCurrentFrame(): getParent().args;
	local btab = {};
	local dtab = {};
	local htab = {};
	local itab = {};
	local ltab = {};
	local ntab = {};
	local otab = {};
	local qtab = {};
	local rtab = {};
	local ttab = {};
	local utab = {};
	local Ctab = {};

	local by0  = ppar.b or ppar.by or ppar.u or ppar.user or "";
	local dis  = ppar.d or ppar.dis or ppar.display or ""; 
	local hil  = ppar.h or ppar.hilite or "";
	local lng  = ppar.i or ppar.lang or "";
	local wik  = ppar.l or ppar.w or ppar.wiki or "";
	local nam  = ppar.n or ppar.name or "";
	local opt  = ppar.o or ppar.opt or ppar.option  or "";
	local mod  = ppar.m or ppar.mod or "";
	local pr4  = ppar.par4 or ppar.qpar or "";
	local pr5  = ppar.par5 or ppar.rpar or "";
	local max  = 0;
	local cor  = 0;
	local x    = 0;  -- running index, can be ~= i
	local fst  = 0;  -- first occurrence
	local plus = "";

	for i, v in ipairs(ppar) do
		if v == "+" then
			plus = "+";
		elseif  v == "-" then
			if plus == "" then plus = "-" end
		else
			x = x + 1;
			if fst == 0 then fst = x end;
			btab [x] = ppar ["b" .. tostring( x )] or ppar ["by" .. tostring( x )] or ppar ["u" .. tostring( x )] or "-";
			dtab [x] = ppar ["d" .. tostring( x )] or "-";
			htab [x] = ppar ["h" .. tostring( x )] or "-";
			itab [x] = ppar ["i" .. tostring( x )] or "-";
			ltab [x] = ppar ["l" .. tostring( x )] or ppar ["w" .. tostring( x )] or "-";
			ntab [x] = ppar ["n" .. tostring( x )] or "-";
			otab [x] = ppar ["o" .. tostring( x )] or "-";
			qtab [x] = ppar ["q" .. tostring( x )] or "-";
			rtab [x] = ppar ["r" .. tostring( x )] or "-";
			ttab [x] = ppar ["t" .. tostring( x )] or "-";
			utab [x] = ppar ["m" .. tostring( x )] or "-";
		end
		max   =  x;
		if v == "x" 
		or v == "X" 
		or v == "×" then
			cor = cor + 1;
		end
	end -- for

	x   = 0;
	if fst > 0 then
		local mnm = mw.text.trim ( ppar [fst] );
	end
	for _, v in ipairs(ppar) do
		if v ~= "+" and v ~= "-" then
			x  =  x + 1;
			local by = by0;  if btab[x] ~= "-" then  by = btab[x]; end
			local ds = dis;  if dtab[x] ~= "-" then  ds = dtab[x]; end
			local hl = hil;  if htab[x] ~= "-" then  hl = htab[x]; end
			local il = wik;  if ltab[x] ~= "-" then  il = ltab[x]; end
			local lg = lng;  if itab[x] ~= "-" then  lg = itab[x]; end
			local op = opt;  if otab[x] ~= "-" then  op = otab[x]; end
			local p4 = pr4;  if qtab[x] ~= "-" then  p4 = qtab[x]; end
			local p5 = pr5;  if rtab[x] ~= "-" then  p5 = rtab[x]; end
			local um = mod;  if utab[x] ~= "-" then  um = utab[x]; end
			local nm = "";   if ntab[x] ~= "-" then  nm = ntab[x]; end
			local tx = "";   if ttab[x] ~= "-" then  tx = ttab[x]; end
			local px = "";
			local vv = mw.text.trim ( v );

			if	mw.ustring.find( vv, "/" ) == nil then
				vv = samefile ( vv, x, mnm );
				if x == 1 then mnm = vv; end
			end

			if	vv ~= "" and vv ~= "×" then
				if ppar.b1 == nil then  -- parameter missing
					px = "";
					if x == 1 then
						px = " "
					elseif x == max then
						table.insert(Ctab, frame:expandTemplate{ title = "Conj-and", args = { lang = 'int:lang' } });
					else
						table.insert(Ctab, frame:expandTemplate{ title = "Comma", args = { lang = 'int:lang' } });
					end
				else					--	ppar.b1 is defined (with value, or empty)
					px = "<br><span style=color:#69F>✦ </span>";     -- 'list' item 
				end
--				ds = mw.text.trim(ds);
				if  mw.ustring.sub(ds, -2) == "px" then
					ds = mw.ustring.sub(ds, 1, mw.ustring.len(ds) -2);
				end
				if nm == "" and x == 1 then nm = nam end
				if hl ~= "" then
					if nm == "" then nm = vv end
					nm = "<" .. hl .. ">" .. nm .. "</" .. hl .. ">"
				end
--				if il ~= ""  then ds = "" end	-- ? (discrepancy)
				if vv == "x" then ds = "" end	-- this should be the last "file"
				if by == ""	 then				-- check for abbreviating "/"
					local sby = mw.ustring.find( vv, "/" );
					if sby ~= nil then 
						by	= mw.ustring.sub( vv, sby+1 ); 
						vv	= mw.ustring.sub( vv, 1, sby-1 ); 
						vv  = samefile ( vv, x, mnm );
						if x == 1 then mnm = vv; end
					end
				end
				table.insert(Ctab, frame:expandTemplate{ title = "F", args = {vv, nm, ds, op, p4, p5, plus, l= il, lang= lg, p= px, by= by, u= um} });
				if tx ~= ""  then
					table.insert(Ctab, frame:expandTemplate{ title = "=", args = { tx } } );			
				end
			end
		end
	end -- for
	if max - cor > 9  then
		table.insert(Ctab, frame:expandTemplate{ title = "Igen/cat", args = {"Own-based with more than 9 files|" .. max} });
	end
 	return  table.concat (Ctab);
end  --  function ownbased


-- horizontal file list for: Template:SVG lang, Template:Lang gallery, and others
function p.svglang (frame) 
	local gpar = frame.args; -- global parms
	local tmpl = gpar [1] or 'Source thumb'; -- 'SVG lang', 'Lang gallery/thumb'
	local ppar = mw.getCurrentFrame():getParent().args;
	local file = ppar.file or '';
	local p = ppar.p or '';
    local trim = mw.text.trim;
    local insert = table.insert;
    local nl = mw.ustring.char(10);
	local results = {};
	for _, v in ipairs(ppar) do
		local lang = trim(v) ; if lang > ' ' then
		    insert(results, trim(frame:expandTemplate{ title = tmpl, args = { file, lang, p = p } }));
		    insert(results, nl);
        end
	end
	return table.concat(results);
end -- function svglang

-- elements count for: Template:SVG lang, Template:Lang gallery, and others
function p.elemct (frame) 
	local ppar = mw.getCurrentFrame():getParent().args;
    local trim = mw.text.trim;
	local count = 0;
	for _, v in ipairs(ppar) do
		local lang = trim(v) ; if lang > ' ' then
			count = count + 1;
		end
	end
	return count
end -- function elemct


-- horizontal file list for: Template:Filelist, Template:File
--   vertical file list for: Template:Other versions, Template:Derived from, Template:Derivative versions 
function p.filelist (frame) 
--  local lpar = frame			--  local parms
	local gpar = frame.args		-- global parms
	local ppar = mw.getCurrentFrame(): getParent().args;
	local ctab = {};
	local ltab = {};
	local ntab = {};
	local dtab = {};
	local otab = {};
	local ptab = {};
	local qtab = {};
	local rtab = {};
	local vtab = {};
	local ktab = {};
	local itab = {};
	local xtab = {};
	local btab = {};
	local stab = {};
	local ttab = {};
	local utab = {};
	local loop = {}; 
	local mnum = 0; 
	local dir  = gpar[1] or "none"
	local nam  = ppar.n             or ppar.name    or ""; 
	local dis  = ppar.d or ppar.z or ppar.dis or ppar.display or ""; 
	local lnk  = ppar.l or ppar.lnk or ppar.link    or "";
	local opt  = ppar.o or ppar.opt or ppar.option  or "";
	local pre  = ppar.p or ppar.pre or ppar.pretext or "";
	local mod  = ppar.m or ppar.mod or "";
	local pr4  = ppar.par4 or ppar.qpar or "";
	local pr5  = ppar.par5 or ppar.rpar or "";
	local pr6  = ppar.par6 or ppar.vpar or "";
	local by0  = ppar.user or ppar.by or "";
	local mod  = ppar.mod or ppar.m  or "";
	local spa  = ppar.spa or ppar.s  or "-"; 
	local wik  = ppar.w or ppar.k or ppar.wiki or ppar.sisterproject or "";
	local int  = ppar.i or ppar.int or ppar.ind or ppar.inter or "";
	local pfx  = ppar.x or ppar.pfx or ppar.prefix  or ""; -- 'list' item {{Comma}}
	local con  = ppar.conj or ppar.con or ppar.c or " ";
	con = mw.ustring.sub(con, 1, 1)
	if  mw.ustring.sub(dis, -2) == 'px' then 
		dis = mw.ustring.sub(dis, 1, mw.ustring.len(dis) -2)
	end
	if ppar.z then
		if con == " " then con = "n" end;
		if opt == ""  then opt = "Z" end;
		if pre == ""  then pre = "&#32;" end;
	end
	local vary = ppar.vary or ppar.v  or "";	-- var pattern
	local var1 = ppar.var1 or ppar.v1 or ""; 	-- var filling space before
	local var2 = ppar.var2 or ppar.v2 or ""; 	-- var filling space after
	if var1 == "space" then var1 = " " end;
	if var2 == "space" then var2 = " " end;

	if dir == "vert" and pfx == "" then
		pfx  = "\n* ";     -- 'list' item
	end

	for i, v in ipairs(ppar) do
		ntab [i] = ppar ["n" .. tostring( i )] or ppar ["l" .. tostring( i )] or "-";
		dtab [i] = ppar ["d" .. tostring( i )] or "-";
		ltab [i] = ppar ["l" .. tostring( i )] or "-";
		otab [i] = ppar ["o" .. tostring( i )] or "-";
		ptab [i] = ppar ["p" .. tostring( i )] or "-";
		qtab [i] = ppar ["q" .. tostring( i )] or "-";
		rtab [i] = ppar ["r" .. tostring( i )] or "-";
		vtab [i] = ppar ["v" .. tostring( i )] or "-";
		ktab [i] = ppar ["k" .. tostring( i )] or "-";
		itab [i] = ppar ["i" .. tostring( i )] or "-";
		xtab [i] = ppar ["x" .. tostring( i )] or "-";
		stab [i] = ppar ["s" .. tostring( i )] or "~";
		ttab [i] = ppar ["t" .. tostring( i )] or "-";
		utab [i] = ppar ["m" .. tostring( i )] or "-";
		btab [i] = ppar ["b" .. tostring( i )] or ppar ["by" .. tostring( i )] or "-";
		mnum  = i;
	end

	local lcnt = 0;				--	loop count
	local i = 1;				--	while index
	while ppar[i] ~= nil do		--	for i, v in ipairs(ppar) do
		local j = 1;
		local vv = mw.text.trim( ppar[i] );
		if vary ~= ""
		and mw.ustring.byte ( vv,  1  ) == mw.ustring.byte ( '#' )	--	loop processing ?
	  	and mw.ustring.byte ( vv, #vv ) == mw.ustring.byte ( '#' ) then 
			local pv = true;	--	"plain"
			loop = mw.text.split( vv, '#', pv );
			local llow = tonumber( loop[2] ) or -1;			
			local lupp = tonumber( loop[3] ) or -1;
			if llow >= 0 and lupp >= llow then
				llow = llow + lcnt;
				vv = tostring ( llow );
				lcnt = lcnt + 1;
				if llow < lupp 
					then j = 0		--	iterate this index
					else lcnt = 0;	--	loop ended
				end;	 
			end
	 	end			-- end loop processing
		if  mw.ustring.byte ( vv,  1  ) == mw.ustring.byte ( '"' )	--	text processing ?
		and mw.ustring.byte ( vv, #vv ) == mw.ustring.byte ( '"' ) 
			 then table.insert(ctab, frame:expandTemplate{ title = "=", args = {mw.ustring.sub(vv, 2, #vv - 1)} });
		elseif vary == "" or i > 1 then
			local nm = nam;  if ntab[i] ~= "-" then  nm = ntab[i];  end
			local ds = dis;  if dtab[i] ~= "-" then  ds = dtab[i];  end
			local ln = lnk;  if ltab[i] ~= "-" then  ln = ltab[i];  end
			local op = opt;  if otab[i] ~= "-" then  op = otab[i];  end
			local pr = pre;  if ptab[i] ~= "-" then  pr = ptab[i];  end
			local p4 = pr4;  if qtab[i] ~= "-" then  p4 = qtab[i];  end
			local p5 = pr5;  if rtab[i] ~= "-" then  p5 = rtab[i];  end
			local p6 = pr6;  if vtab[i] ~= "-" then  p6 = vtab[i];  end
			local pk = wik;  if ktab[i] ~= "-" then  pk = ktab[i];  end
			local pi = int;  if itab[i] ~= "-" then  pi = itab[i];  end
			local px = pfx;  if xtab[i] ~= "-" then  px = xtab[i];  end
			local sp = spa;  if stab[i] ~= "~" then  sp = stab[i];  end
			local tx = "";   if ttab[i] ~= "-" then  tx = ttab[i];  end
			local by = by0;  if btab[i] ~= "-" then  by = btab[i];  end
			local um = mod;  if utab[i] ~= "-" then  um = utab[i];  end

			if vary ~= "" then
				if sp == "+" then sp = "&#32;" 
				else sp = "" end; 
				vv = mw.ustring.gsub( ppar [1], vary, var1..sp..vv..var2 )
			else
				vv = samefile ( vv, i,  ppar [1] )
			end	
			if vv ~= "" and vv ~= "×" then
				if dir == "hori" and con ~= "n" then	-- horizontal list
					px = "";
					if i == 1 then 
						px = "&#32;"
					elseif i == mnum then
						table.insert(ctab, frame:expandTemplate{ title = "Conj-and", args = { lang = 'int:lang' } });
					else
						table.insert(ctab, frame:expandTemplate{ title = "Comma", args = { lang = 'int:lang' } });
					end
				else		--	elseif dir == "vert"ical then
					if i == 1 then 
						px = "&#32;"
					end
				end
				if	by == "" then					-- check for abbreviating "/"
					local sby = mw.ustring.find( vv, "/" );
					if sby ~= nil then 
						by	= mw.ustring.sub( vv, sby+1 ) 
						vv	= mw.ustring.sub( vv, 1, sby-1 ) 
					end
				end
				if ln == "" then
					table.insert(ctab, px .. frame:expandTemplate{ title = "F", args = {vv, nm, ds, op, p4, p5, p6, l= pk, lang= pi, p=pr, by= by, u= um} });
				else
					table.insert(ctab, px .. frame:expandTemplate{ title = "F", args = {vv, nm, ds, op, p4, p5, p6, l= pk, lang= pi, p=pr, by= by, u= um, link= ln} });
				end 
				if tx ~= ""  then
					table.insert(ctab, frame:expandTemplate{ title = "=", args = { tx } } );
				end
			end		-- if vary
		end			-- if vv
		i = i + j;	-- next in do loop
	end				-- while
 	return  table.concat (ctab)
end -- function filelist


-- for Template:Attribs  (param pairs; but also for single params)
function p.attribs (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local ftab = {};	-- "from" topic
	local ptab = {};	-- parts
	local mtab = {};	-- modification
	local rtab = {};	-- work tab		
	local otab = {};	-- output
	local fnum = 0;		
	local rnum = 0;
	local hnum = 0;
	local tp   = ppar.type or "SVG";  -- needs check
	local un   = ppar.by or ppar.U or ppar.u or "";
	local md   = ppar.m or ppar.mod or "";
	local tt   = ppar.t or ppar.to or "";		-- "to" topic
	local f    = ppar.f or ppar.from or tt;		-- from" 
	local p    = ppar.p or ppar.part or "";
	local s    = ppar.s or ppar.style or "s";	-- default
	local vx   = "";
	local ux   = "";
	local ft   = "";
	local um   = "";

	for i, v in ipairs(ppar) do
		fnum = fnum + 1;   -- input parm number
		if fnum % 2 ==  0 then  -- even: should be a username
			local enam = mw.text.trim ( v );
			local snam = mw.ustring.lower ( mw.ustring.sub ( enam, -4 ) );
			if snam == ".svg"
			or snam == ".png"
			or snam == ".jpg"			-- a rough check: is_extension ?
			or snam == ".gif"
			or snam == ".pdf"
			or snam == ".tif" then		-- no - it's the next filename
				table.insert(rtab, "" ); -- empty username inbetween
				rnum = rnum + 1;
				fnum = fnum + 1;		-- make it odd
			end
		end
		if fnum % 2 ==  1 then  --  odd (now): is a filename
			hnum = fnum + 1 
			hnum = hnum / 2 
			ftab [hnum] = ppar ["f" .. tostring( hnum )] or ".";
			ptab [hnum] = ppar ["p" .. tostring( hnum )] or ".";
			mtab [hnum] = ppar ["m" .. tostring( hnum )] or ".";
		end
--                                   table.maxnum (ppar) does not work
--                                   therefore the "rtab" workaround
		table.insert(rtab, ppar[i]); -- = enam
		rnum = rnum + 1
	end
--	                                 plus one item when odd number
	if  rnum % 2 ==  1 then
 		table.insert( rtab, "" ); -- empty user name, to get a pair
	end
--	
	for i, v in ipairs(rtab) do
		if vx == "" then     -- the 2nd 'v' can be empty
			vx = mw.text.trim( v );
		else
			ux = mw.text.trim( v );
			if ux == "" then ux = un end; -- does not work otherwise (?)
			hnum = i + 0 
			hnum = hnum / 2 
			if ftab[hnum] == "." then 
				ft = f;
			else
				ft = ftab[hnum]  
			end
			if ptab[hnum] == "." then 
				pt = p;
			else
				pt = ptab[hnum]  
			end
			if mtab[hnum] == "." then 
				um = md;
			else
				um = mtab[hnum]  
			end
			table.insert(otab, frame:expandTemplate{ title = "Attrib", args = {vx, ux, "-", tp, "", ux, tt, ft, pt, m=um, s=s } });
			vx = "";
		end
	end
	return  table.concat (otab)
end -- function attribs


-- get the user id: the (last) parameter which is prefixed by '/'
function p.byuser ( frame )
--	local gpar = frame.args-- global parms;
	local ppar = mw.getCurrentFrame(): getParent().args;
	local user = '';
	for _, value in  pairs( ppar ) do
		if value ~= nil and		-- user == '' and
		   mw.ustring.sub  (value, 1, 1) == '/' then
			user = mw.ustring.sub (value, 2)	-- remove the "/"
		end
-- 		test whether userID exists ...
	end
	return user
end --  function byuser, for template:F 

 
-- for Template:userlist  (horizontal - but vertical when dir=I/O/U/V)
function p.userlist (frame)
--	local lpar = parm       --  local parms
	local gpar = frame.args -- global parms (par/P,dir/V)
	local ppar = mw.getCurrentFrame(): getParent().args;
	local ctab = {};		-- case
	local htab = {};		-- hilite
	local ltab = {};		-- link option
	local ntab = {};		-- 2 display name
	local wtab = {};		-- 3 interwiki 
	local otab = {};		-- 4 +/- option
	local mtab = {}; 		-- u_mod c/t/w/wt (aka utab, mtab, ptab)
	local ttab = {};		-- translate i18n
--@	local rtab = {};
--@	local qtab = {};		
	local xtab = {};		-- postfix text
	local Itab = {};		-- the output table

	local cas  = ppar.c or ppar.case or "";
	local hil  = ppar.h or ppar.hilite or "";
	local lnk  = ppar.l or ppar.link or "";
	local nam  = ppar.n or ppar.name or "";
	local opt  = ppar.o or ppar.opt  or ppar.option or "";
	local mod  = ppar.m or ppar.mod  or ppar.user or ppar.u or ppar.page or ppar.p or "";
--@	local pr4  = ppar.q or ppar.qpar or ppar.par4 or "";
--@	local pr5  = ppar.r or ppar.rpar or ppar.par5 or "";
	local trl  = ppar.t or ppar.i18n or ppar.translate or "";
	local wik  = ppar.w or ppar.wiki or ppar.lang or "";

	local max  = 0;
	local cor  = 0;
	local x    = 0;  -- running index, can be ~= i
	local y    = 0;  -- running index, can be ~= i
	local odd  = 0;
	local out  = 0;
	local plus = ""; -- opt

	local prim = ""
	local prfx = ":"
	local pend = ""
	if gpar.dir == "O" then		-- ordered list
		prim = "<ol>";
		prfx = "<li>";
		pend = "</ol>";
	end	
	if gpar.dir == "U" then		-- unordered list
		prim = "<ul>";
		prfx = "<li>";
		pend = "</ul>";
	end
	if gpar.dir == "V" then		-- vertical list
		prim = " ";
		prfx = "<br><span style=color:#69F>✦ </span>";
	end	

	for _, v in ipairs(ppar) do
		v = mw.text.trim ( v );
		if v == "+" then
			plus = "+";
		elseif  v == "-" then
			if plus == "" then plus = "-" end
		else
			y = y + 1;
			if gpar.par == "P"	-- pairs of (user, name)
			and y % 2 == 0 then	-- 1=user, 0=name
				x = x;		
			else
				x = x + 1;
				ctab [x] = ppar ["c" .. tostring( x )] or "-";
				htab [x] = ppar ["h" .. tostring( x )] or "-";
				ltab [x] = ppar ["l" .. tostring( x )] or "-";
				ntab [x] = ppar ["n" .. tostring( x )] or "-";
				otab [x] = ppar ["o" .. tostring( x )] or "/";
				mtab [x] = ppar ["m" .. tostring( x )] or ppar ["u" .. tostring( x )] or ppar ["p" .. tostring( x )] or "-";
--@				rtab [x] = ppar ["r" .. tostring( x )] or "-";
--@				qtab [x] = ppar ["q" .. tostring( x )] or "-";
				ttab [x] = ppar ["t" .. tostring( x )] or "-";
				wtab [x] = ppar ["w" .. tostring( x )] or "-";
				xtab [x] = ppar ["x" .. tostring( x )] or "-";
			end
		end
		max  = x;
	end -- for
	odd  = y % 2; -- 1 when last one not paired

	if plus > "" and opt == "" then
		opt = plus;		-- discrepancy ? opt has priority
	end
	x   = 0;
	y   = 0;
	local vv = "";
	for _, v in ipairs(ppar) do
		v = mw.text.trim ( v );
		if v ~= "+" and v ~= "-" then
			y = y + 1;
			if gpar.par == "P"	-- pairs of (user, name)
			and y % 2 == 1 		-- 1=user, 0=name 
			and odd == 0 then	-- last one paired
				vv = v;	 		-- userid
			else
				x  =  x + 1;
				local cs = cas;  if ctab[x] ~= "-" then  cs = ctab[x]; end
				local hl = hil;  if htab[x] ~= "-" then  hl = htab[x]; end
				local lk = lnk;  if ltab[x] ~= "-" then  lk = ltab[x]; end
				local nm = nam;  if ntab[x] ~= "-" then  nm = ntab[x]; end
				local op = opt;  if otab[x] ~= "/" then  op = otab[x]; end
				local md = mod;  if mtab[x] ~= "-" then  md = mtab[x]; end
--@				local p4 = pr4;  if rtab[x] ~= "-" then  p4 = rtab[x]  end
--@				local p5 = pr5;  if qtab[x] ~= "-" then  p5 = qtab[x]  end
				local tr = trl;  if ttab[x] ~= "-" then  tr = ttab[x]; end
				local wk = wik;  if wtab[x] ~= "-" then  wk = wtab[x]; end
				local tx = "";   if xtab[x] ~= "-" then  tx = xtab[x]; end

				if gpar.par == "P"	then	--	y%2 = 0 (name)
					if odd == 1	then	--	no: last user:
						vv = v;			--	user
					else
						nm = v;			--	name
					end
				else 
					vv = v;
				end
				if hl ~= "" then
					if nm == "" then nm = vv; end
					nm = "<" .. hl .. ">" .. nm .. "</" .. hl .. ">";
				end
				local p1 = "";  -- prefix
				local p2 = "";  -- postfix
				if md == "t" or md == "wt" or md == "tw" then	p1 = "&#32;talk";	 end
				if md == "w" or md == "wt" or md == "tw" then
					if wk == "" then 	p2 = "~commonswiki";
					else 				p2 = "~"..wk.."wiki";
					end
				end
				if md == "n" or md == "no" then
					lk = md;					-- no link
				end

				if vv ~= "" and vv ~= "×" then
					if md == "c"  then	vv = "Special:Contributions/" .. vv; end
					if gpar.dir == "I" 
					or gpar.dir == "O" 
					or gpar.dir == "U" 
					or gpar.dir == "V" then  -- vertical list
						if out == 0 then
							table.insert(Itab, frame:expandTemplate{ title = "=", args = { prim } });
						end
						table.insert    (Itab, frame:expandTemplate{ title = "=", args = { prfx } });
						out = out + 1;
					else						--	
						if x == 1 then
							x = 1		--	??? horizontal list initializing (text)
						elseif x == max then
							table.insert(Itab, frame:expandTemplate{ title = "Conj-and", args = { lang = 'int:lang' } });
						else
							table.insert(Itab, frame:expandTemplate{ title = "Comma", args = { lang = 'int:lang' } });
						end
					end

					if 	mw.ustring.sub( vv, 1, 2 ) == '{{'
					or 	mw.ustring.sub( vv, 1, 2 ) == '[[' then	
						table.insert(Itab, frame:expandTemplate{ title = "=", args = { vv } });		-- 
					else
						table.insert(Itab, frame:expandTemplate{ title = "U/main", args = { vv, nm, wk, op, link=lk, par1=p1, par2=p2, case=cs, i18n=tr} });
					end 
					if tx ~= ""  then
						table.insert(tab, frame:expandTemplate{ title = "=", args = { tx } } );			
					end
				end
			end
		end
	end -- for

	if	out > 0 and pend > " " then
		table.insert(Itab, frame:expandTemplate{ title = "=", args = { pend } });
	end
	return  table.concat (Itab);
end  --  function userlist


--  Table for templates:  Legend; Legend-line, Legend2, Legend-small ...  (param pairs)
function p.legendt (frame)
	local frmp = frame.args
	local temp = frmp [1] or "Legend"
	local parm = frmp [2] or ""
	local wide = frmp [3] or ""
	local cssp = ""		-- dummy parm
	local ppar = mw.getCurrentFrame(): getParent().args;
	local ttip = ppar.tt or ppar.ttip or ppar.tooltipping or ""
	local otab = {};
	local lg = ppar.lang or "";
	local v1 = ""
	for _, v in ipairs(ppar) do
		if v1 == "" then   
			v1 =  v;
			if ttip == "yes" then		-- tooltip rhe colors
				cssp = '"title="'..mw.ustring.upper(v1)
			end
		else
			table.insert(otab, frame:expandTemplate{ title = temp, args = { v1, v, lang = lg, p = parm, size = wide, css = cssp } });
			v1 = "";
		end	
	end
	if v1 ~= "" then		--	the problem when not a last pair
		table.insert(otab, frame:expandTemplate{ title = temp, args = { v1, "", lang = lg, p = parm, size = wide, css = cssp } });	-- last elem
	end
	return  table.concat (otab)
end --  function legendt


-- for Template:ColorString (Igen/cbox   -   param pairs)
function p.colorbox (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	local lg = ppar.lang or "{{PAGELANGUAGE}}"; --
	local v1 = "";
	local p2 = "";
	local ct = 0;
	local bp = 0;
	local bc = '';
	local cf = '';
	for i, v in ipairs(ppar) do
		if i <= 2 then 
			p2 = v;							-- 	either code-2 ( BCDLMST-), or color value  
		else	
			if v1 == "" then   
				v1 = mw.text.trim( v );		--	( striped; border )
				ct = 0;						-- 	count of spaces
				bp = 0;						-- 	position of first space
				for c = 1, #v1 do
					if mw.ustring.sub( v1, c, c) == ' ' then
						ct = ct + 1;		-- space count
						if bp == 0 then bp = c;	end
					end
				end
				bc = '';
				if ct >= 3 then				-- 3=border, 4=line
					bc = mw.ustring.sub(v1, bp+1);
					v1 = mw.ustring.sub(v1, 1, bp-1);
				end
				if ct == 1 then				-- char or file
					bc = mw.ustring.sub(v1, bp+1);
					v1 = mw.ustring.sub(v1, 1, bp-1);
					if  mw.ustring.sub ( bc, -4, -4 ) == "." then
						cf = 'file'
					else
						cf = 'char'
					end
				end
			else
				table.insert(otab, frame:expandTemplate{ title = "Igen/cbox", args = { v1, v, p2, ct, bc, cf, lang = lg } });
				v1 = "";
			end
		end
	end
	if v1 ~= "" then
		table.insert(otab, frame:expandTemplate{ title = "Igen/cbox", args = { v1, "", p2, ct, bc, cf, lang = lg } });	-- last elem		
	end
	return  table.concat (otab);
end --  function colorbox

--------------------------------------------------------------------------------
-- returns a parameter list: (replacement of «#» or «~» by «=»:  not necessary)
function p.plist ( frame )
	local gpar = frame.args -- global parms
	local ppar = mw.getCurrentFrame(): getParent().args;
	local ostr = ""
	for i, v in ipairs(ppar) do
		if i < 2 then 
			ostr = v; 
		else	
			ostr = ostr .. "|" .. v; 
		end	
	end
	return ostr;	--	return mw.ustring.gsub( ostr, "#", "=" ),_ not necessary
end --  function plist

-- repeats a text string
function p.loop ( frame )					-- repeat
	local ppar = mw.getCurrentFrame(): getParent().args;
	local pnum = 0;							-- cunt
	local pchr = ppar[2] or '&nbsp';		-- char
	local rval = '';				

	pnum = pnum + tonumber(ppar[1] or 1);	-- cunt
	for l = 1, pnum do
		rval = rval .. pchr;
	end
	return  rval;
end -- function loop

-- increment a hex number by a ±decimal value
function p.incrhx ( frame )
	local gpar = frame.args-- global parms
	return mw.ustring.format ( "%X", tonumber ( gpar[1], 16 ) + gpar[2] )
end --  function incrhx

-- display an emoji character
function p.emodis ( frame )
	local gpar = frame.args-- global parms;
	local ucod = mw.ustring.format ( "%X", tonumber ( gpar[1], 16 ) + gpar[2] );
	local newl = "";
	if mw.ustring.sub ( ucod, -1, -1 ) == 'F'
		then newl = "&#8203;";
			if gpar[3] == "16"
				then newl = "<br>";
			end
	end
	return '"U+'..ucod..'">&#x'..ucod..';'..newl
end --  function emodis 

-- for Template:Emoji table: {{#invoke:Iteration|emotab|prefix=1F250|par1= ...| ... }}
function p.emotab (frame) 
--	local lpar = parm       --  local parms
	local gpar = frame.args -- global parms
--	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	local part = {};
	local case = " ";
	local codp = gpar.codp or nil;
	part[2] = gpar.par1 or "";	--	Z 
	part[3] = gpar.par2 or "";	--	49	
	part[4] = gpar.par3 or "";	--	b
	part[5] = gpar.par4 or "";	--	l
	part[6] = gpar.par5 or "";	--
	part["link"] = "*"			-- link=*
	for i = 2, 6  do
		if part[i] == ""  then  part[i] = nil end;
	end; 
	for _, v in ipairs(gpar) do
		if mw.ustring.sub(v, 1, 6) == "Emojio" 
		or mw.ustring.sub(v, 1, 6) == "Fxemoj"
		or mw.ustring.sub(v, 1, 6) == "OpenMo"
			then case = mw.ustring.upper(codp);
			else case = mw.ustring.lower(codp);
		end
 		if mw.title.new(v..case..".svg", 6).exists  then
			part[1] = v..case..".svg"
--			table.insert(otab, frame:expandTemplate{ title = "=", args = {" | "} } );
			table.insert(otab, frame:expandTemplate{ title = "F", args = part } );
		end
	end
	return table.concat (otab)
end -- function emotab


-- for template: Navcat (via Navcattab)
function p.navcat ( frame )
--  local lpar = frame     --  local parms
	local gpar = frame.args-- global parms
	local ppar = mw.getCurrentFrame(): getParent().args;
	local cat1 = gpar.c1 or "";
	local cat2 = gpar.c2 or "";
	local span = gpar.span or "";
	local otab = {};
	for _, v in ipairs(ppar) do
		v = mw.text.trim(v);
		if v == "" or v == "nl" then 
--	((		table.insert(otab, frame:expandTemplate{ title="clr", args = { "" } } );
			table.insert(otab, frame:expandTemplate{ title="=", args = { "<br>" } } );
		else 
			local itab = mw.text.split ( v, "/" );
			local ctxt = itab[1]; -- requ
			local ccod = itab[2]; -- opt.
			local cnam = itab[3] or ccod or ctxt;
			local dtxt = mw.ustring.upper(mw.ustring.sub(ctxt,1,1))..mw.ustring.sub(ctxt,2); -- ucfirst
			table.insert(otab, frame:expandTemplate{ title="Navcat", args={ccod, cat1..ctxt..cat2, dtxt, cnam, span=span } } );
		end
	end
 	return  table.concat (otab);
end --  function navcat


-- Template:Tle  
function p.tleparm (frame) 
--  local lpar = frame     -- local parms
--	local gpar = frame.args-- global parms
	local ppar = mw.getCurrentFrame(): getParent().args;
	local par1 = "";
	local par3 = ppar.f      or ""; -- form feed
	local par4 = ppar.style  or ""; -- style for value
	local par5 = ppar.style2 or ""; -- style for param
	local numb = ppar.number or "";
	local kybd = ppar.kbd    or "";
	local ctab = {}

	numb = mw.text.trim( numb );
	for i, v in ipairs(ppar) do
		par1 = ppar ["p" .. tostring( i )] or "";
		if par1 ~= "" and kybd ~= "" then 
			par1 = '<kbd>'..par1..'</kbd>'
		end
		if par1 == "" and numb ~= "" then 
			par1 = tostring( i )
		end
		if mw.text.trim(v) == "" then v = "&thinsp;" end
		table.insert( ctab, frame:expandTemplate{ title = "Tle/parm",  args = {  par1, v, par3, par4, par5} } );
	end
 	return  table.concat (ctab)
end --  function tleparm

-- Template:variate 
function p.variation (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local strg = ppar [1]  or "";	-- sample string
	local pttn = ppar.pttn or "?";	-- pattern
	local var1 = ppar.var1 or ""; 	-- filling space before
	local var2 = ppar.var2 or ""; 	-- filling space after
	local sept = ppar.sept or "|"; 	-- separator 
	local sep2 = ppar.sep2 or ""; 	-- separator after
	if var1 == "space" then var1 = " " end;
	if var2 == "space" then var2 = " " end;
	local retv = "";
	strg = mw.text.trim( strg )
	
	for i, v in ipairs(ppar) do
		if i > 1 then 
			retv = retv .. sept .. mw.ustring.gsub( strg, pttn, var1..mw.text.trim(v)..var2 ) .. sep2 
		end
	end	
	return retv
end	

return p;