Module:Iteration

From lisamarieyoung.ca

Documentation for this module may be created at Module:Iteration/doc

--  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 titl = gpar [1] or "Source thumb"  -- SVG lang; "Lang gallery thumb";
	local ppar = mw.getCurrentFrame(): getParent().args;
	local otab = {};
	local file = ppar.file or "";
	for _, v in ipairs(ppar) do
		table.insert(otab, frame:expandTemplate{ title = titl, args = { file, mw.text.trim(v) } });
	end
	return  table.concat (otab)
end -- function svglang

-- elements count:  Template:SVG lang,  and others
function p.elemct (frame) 
	local ppar = mw.getCurrentFrame(): getParent().args;
	local cunt = 0;
	for _, v in ipairs(ppar) do
		if v > ' ' then 
			cunt = cunt + 1;
		end
	end
	return cunt
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;