-- This module contains the functions for different templates, solving their iteration problems.
local max = math.max
local len = string.len
local sub = string.sub
local find = string.find
local format = string.format
local insert = table.insert
local concat = table.concat
local trim = mw.text.trim
local split = mw.text.split
local titleNew = mw.title.new
local char = mw.ustring.char
local NL = char(10)
local lower = mw.ustring.lower
local upper = mw.ustring.upper
local gsub = mw.ustring.gsub
local Usub = mw.ustring.sub
local function Ucfirst(text)
text = trim(text)
return upper(Usub(text, 1, 1)) .. Usub(text, 2)
end
----------------------------------------------------------------------------------------------
-- Template expansion helpers for performance:
-- Saves CPU and time resources on large iterations by minimizing repeated recursive calls to
-- the parser, eliminating many table constructions/deletion/lookups in memory. Also allows
-- reporting CPU/time usage for expansions of costly templates inside recursive calls
-- (otherwise all CPU/time is counted inside Lua, those templates are not counted).
local currentTitle = mw.title.getCurrentTitle()
local currentNs = tonumber(currentTitle.namespace)
local currentPage = currentTitle.text
local frameCurrent = mw.getCurrentFrame()
local expandTemplateCurrent = frameCurrent.expandTemplate
local callParserFunctionCurrent = frameCurrent.callParserFunction
local contentLanguage = mw.language.getContentLanguage()
local langDefault = contentLanguage:getCode()
local langUser = lower(trim(callParserFunctionCurrent(frameCurrent, 'Int', 'Lang')))
local transclude = {}
local function expand(template, args)
transclude.title = template
transclude.args = args or {}
return expandTemplateCurrent(frameCurrent, transclude)
end
local argsOne = {}
local function expand1(title, arg1)
argsOne[1] = arg1
return expand(title, argsOne)
end
local argsLangUser = { lang = langUser }
local argsLangDefault = { lang = langDefault }
local argsLangOther = {}
local function expandWithLang(template, lang)
if type(lang) == 'string' then
lang = trim(lang)
if lang == '' then
lang = nil
else
lang = lower(lang)
end
else
lang = nil
end
if not lang or lang == langUser then
return expand(template, argsLangUser)
elseif lang == langDefault then
return expand(template, argsLangDefault)
else
argsLangOther.lang = lang
return expand(template, argsLangOther)
end
end
local Comma, ConjAnd
local function commaAnd(isLast)
if isLast then
if not ConjAnd then
ConjAnd = expand('Conj-and', argsLangUser)
end
return ConjAnd
else
if not Comma then
Comma = expand('Comma', argsLangUser)
end
return Comma
end
end
----------------------------------------------------------------------------------------------
local p = {}
-- For Template:Ifim.
local argsIfim1 = { -- Parameters for expanding 'Template:Ifim1'.
'', -- [1] = v
'', -- [2] = trim(ppar.p1 or ''),
'', -- [3] = trim(ppar.p2 or ''),
'', -- [4] = trim(ppar.p3 or ''),
'', -- [5] = trim(ppar.p4 or ''),
'', -- [6] = trim(ppar.p5 or ''),
n = '' -- ['n'] = trim(ppar.fn or ''),
}
function p.ifim1(frame)
local ppar = frameCurrent:getParent().args
argsIfim1[2] = trim(ppar.p1 or '')
argsIfim1[3] = trim(ppar.p2 or '')
argsIfim1[4] = trim(ppar.p3 or '')
argsIfim1[5] = trim(ppar.p4 or '')
argsIfim1[6] = trim(ppar.p5 or '')
argsIfim1.n = trim(ppar.fn or '')
local results = {}
for _, v in ipairs(ppar) do
argsIfim1[1] = v
insert(results, expand('Ifim1', argsIfim1))
end
return concat(results)
end -- function ifim
-- For Template:Ifimc.
local argsIfim2 = { -- Parameters for expanding 'Template:Ifim2'.
'', -- [1] = v1
'', -- [2] = trim(ppar.p1 or ''),
'', -- [3] = trim(ppar.p2 or ''),
'', -- [4] = trim(ppar.p3 or ''),
'', -- [5] = trim(ppar.p4 or ''),
'', -- [6] = v
}
function p.ifim2(frame)
local ppar = frameCurrent:getParent().args
argsIfim2[2] = trim(ppar.p1 or '')
argsIfim2[3] = trim(ppar.p2 or '')
argsIfim2[4] = trim(ppar.p3 or '')
argsIfim2[5] = trim(ppar.p4 or '')
local results = {}
for _, v in ipairs(ppar) do
argsIfim2[1] = v
argsIfim2[6] = v
insert(results, expand('Ifim2', argsIfim2))
end
return concat(results)
end -- function ifim2
-- For Template:Ifimt (param pairs).
function p.ifimt(frame)
local ppar = frameCurrent:getParent().args
argsIfim2[2] = trim(ppar.p1 or '')
argsIfim2[3] = trim(ppar.p2 or '')
argsIfim2[4] = trim(ppar.p3 or '')
argsIfim2[5] = trim(ppar.p4 or '')
local v1 = ''
local results = {}
for _, v in ipairs(ppar) do
if v1 == '' then
v1 = v
else
argsIfim2[1] = v1
argsIfim2[6] = v
insert(results, expand('Ifim2', argsIfim2))
v1 = ''
end
end
if v1 ~= '' then
argsIfim2[6] = ''
insert(results, expand('Ifim2', argsIfim2)) -- Last item.
end
return concat(results)
end -- function ifimt
-------------------------------------------------------
-- Helper function for: ownbased, and filelist
local function samefile(filename, num, first)
local filename = trim(filename or '')
if sub(filename, 1, 1) == '.' then
local part = 'Example' -- default filename
if currentNs == 6 or currentNs == 7 then
part = split(currentPage , '.', true)
part = concat(part, '.', 1, math.max(#part - 1, 1))
end
-- Supported short aliases:
if filename == '.' then filename = part .. '.png'
elseif filename == '.p' then filename = part .. '.png'
elseif filename == '.g' then filename = part .. '.gif'
elseif filename == '.j' then filename = part .. '.jpg'
elseif filename == '.s' then filename = part .. '.svg'
-- Exotics:
elseif filename == '.m' then filename = part .. '.mid'
elseif filename == '.o' then filename = part .. '.ogg'
elseif filename == '.t' then filename = part .. '.tif'
elseif filename == '.v' then filename = part .. '.wav'
elseif filename == '.x' then filename = part .. '.xcf'
-- Note: no "short alias" for '.pdf' ('.p' already used for '.png')
-- Note: short aliases for a few longer extensions:
elseif filename == '.d' then filename = part .. '.djvu'
elseif filename == '.w' then filename = part .. '.webp'
else filename = part .. filename
end
elseif filename == '' or filename == '*' then
if tonumber(num) == 1 then
filename = 'Example.svg'
if currentNs == 6 or currentNs == 7 then
filename = currentPage
end
else
filename = trim(first)
if filename == '' or filename == '*' or sub(filename, 1, 1) == '.' then
filename = 'Example.svg'
if currentNs == 6 or currentNs == 7 then
filename = currentPage
end
end
end
end
return filename
end -- function samefile
---++++++++++++++++++++++++++++++++++++++++++++++++++++
-- Simple iterations - without many params.
function p.iterate(frame)
local gpar = frame.args -- global parms
local template = gpar[1] or '' -- template name
local args = { -- Parameters for expanding the given template.
'', -- [1] = v
gpar[2] or '',
gpar[3] or '',
}
local ppar = frameCurrent:getParent().args
local results = {}
for _, v in ipairs(ppar) do
args[1] = v
insert(results, expand(template, args))
end
return concat(results)
end -- function iterate
-- More iterations - for params, param pairs, or n-tuples.
function p.iteration(frame)
local gpar = frame.args -- Global parms.
local vmax = tonumber(gpar.n) or 2 -- Tuple number (default = 2).
local vnum = vmax + 1 -- Additional not-changing params.
local template = '' -- Template name.
local args = {} -- Parameters for expanding the given template.
for i, p in ipairs(gpar) do
p = trim(p)
if i == 1 then
template = p
else
args[vnum] = p
vnum = vnum + 1
end
end
vnum = 1
local ppar = frameCurrent:getParent().args
local results = {} -- output
for _, v in ipairs(ppar) do
args[vnum] = trim(v)
vnum = vnum + 1
if vnum > vmax then
insert(results, expand(template, args))
vnum = 1
end
end
return concat(results)
end -- function iteration
-- For different templates, for general use, e.g. Emoji.
function p.parlst(frame)
local gpar = frame.args -- Global parameters.
local template = trim(gpar.temp or '')
local results = {}
if template ~= '' then -- Slow expansion (e.g. for template = 'Emoji', will recurse into iterations calling p.emodis)
local ff = trim(gpar.ff or '') -- Inbetween?
if ff ~= '' then
ff = expand1(ff, trim(gpar.pf or ''))
else
ff = nil
end
local nocat = trim(gpar.nocat or '')
local args = { -- Parameters for expanding the given template.
nocat = nocat ~= '' and nocat or nil
}
for k, v in pairs(gpar) do
v = trim(v)
if v ~= '' then args[k] = v end
end
local count = tonumber(args[1] or '') or 0
for i = 1, (count or 1) do -- default: go 1× for nil
if ff then insert(results, ff) end
if count then args[1] = i - 1 end
insert(results, expand(template, args))
end
else -- Template expansion not needed (much faster, without recursed iterations calling p.emodis from that template).
local count = tonumber(gpar[1] or '') or 0
local code = tonumber(gpar[2] or '', 16) or 0
local args = {
'<span title="U+',
'', -- [2] = format('%04X', code + i - 1)
'">',
'', -- [4] = char(code + i - 1)
'</span>',
}
for i = 1, count do
args[2] = format('%04X', code + i - 1)
args[4] = char(code + i - 1)
insert(results, concat(args))
end
end
return concat(results)
end -- function parlst
-- Global function for one filename.
function p.filename(frame)
local gpar = frame.args -- Global parameters.
return samefile(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 parameters.
return expand('F', {
samefile(gpar[1] or '.', 1),
by = trim(gpar[2] or '')
})
end -- function ownbasby
-- For Template:Own based (horizontal - but vertical when "b1=").
function p.ownbased(frame)
local ppar = frameCurrent:getParent().args
local by0 = trim(ppar.b or ppar.by or ppar.u or ppar.user or '')
local dis = trim(ppar.d or ppar.dis or ppar.display or '')
local hil = trim(ppar.h or ppar.hilite or '')
local lng = trim(ppar.i or ppar.lang or '')
local wik = trim(ppar.l or ppar.w or ppar.wiki or '')
local nam = trim(ppar.n or ppar.name or '')
local opt = trim(ppar.o or ppar.opt or ppar.option or '')
local mod = trim(ppar.m or ppar.mod or '')
local pr4 = trim(ppar.par4 or ppar.qpar or '')
local pr5 = trim(ppar.par5 or ppar.rpar or '')
local btab = {}
local dtab = {}
local htab = {}
local itab = {}
local ltab = {}
local ntab = {}
local otab = {}
local qtab = {}
local rtab = {}
local ttab = {}
local utab = {}
local x = 0 -- running index, can be ~= i
local fst = 0 -- first occurrence
local cor = 0
local max = 0
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
local z = tostring(x)
btab[x] = trim(ppar['b' .. z] or ppar['by' .. z] or ppar['u' .. z] or '-')
dtab[x] = trim(ppar['d' .. z] or '-')
htab[x] = trim(ppar['h' .. z] or '-')
itab[x] = trim(ppar['i' .. z] or '-')
ltab[x] = trim(ppar['l' .. z] or ppar['w' .. z] or '-')
ntab[x] = trim(ppar['n' .. z] or '-')
otab[x] = trim(ppar['o' .. z] or '-')
qtab[x] = trim(ppar['q' .. z] or '-')
rtab[x] = trim(ppar['r' .. z] or '-')
ttab[x] = trim(ppar['t' .. z] or '-')
utab[x] = trim(ppar['m' .. z] 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 = trim(ppar[fst])
end
local hls = {
'<',
'', -- [2] = hl
'>',
'', -- [4] = (nm ~= '') and nm or vv
'</',
'', -- [6] = hl
'>',
}
local args = { -- Parameters for expanding 'Template:F'.
'', -- [1] = vv,
'', -- [2] = nm
'', -- [3] = ds
'', -- [4] = op
'', -- [5] = p4
'', -- [6] = p5
plus,
l = '', -- ['l'] = il
lang = '', -- ['lang'] = lg
p = '', -- ['p'] = px
by = '', -- ['by'] = by
u = '', -- ['u'] = um
}
local results = {}
for _, v in ipairs(ppar) do
if v ~= '+' and v ~= '-' then
x = x + 1
local by = btab[x] ~= '-' and btab[x] or by0
local ds = dtab[x] ~= '-' and dtab[x] or dis
local hl = htab[x] ~= '-' and htab[x] or hil
local il = ltab[x] ~= '-' and ltab[x] or wik
local lg = itab[x] ~= '-' and itab[x] or lng
local op = otab[x] ~= '-' and otab[x] or opt
local p4 = qtab[x] ~= '-' and qtab[x] or pr4
local p5 = rtab[x] ~= '-' and rtab[x] or pr5
local um = utab[x] ~= '-' and utab[x] or mod
local nm = ntab[x] ~= '-' and ntab[x] or ''
local tx = ttab[x] ~= '-' and ttab[x] or ''
local vv = trim(v)
if find(vv, '/') == nil then
vv = samefile(vv, x, mnm)
if x == 1 then mnm = vv end
end
if vv ~= '' and vv ~= '×' then
local px = ''
if ppar.b1 == nil then -- parameter missing
if x == 1 then
px = ' '
else
insert(results, commaAnd(x == max))
end
else -- ppar.b1 is defined (with value, or empty)
px = '<br /> <span style=color:#69F>✦ </span>' -- "list" item
end
if sub(ds, -2) == 'px' then
ds = sub(ds, 1, -3)
end
if nm == '' and x == 1 then
nm = nam
end
if hl ~= '' then
hls[2] = hl
hls[4] = (nm ~= '') and nm or vv
hls[6] = hl
nm = concat(hlparts)
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 = find(vv, '/')
if sby ~= nil then
by = sub(vv, sby + 1)
vv = sub(vv, 1, sby - 1)
vv = samefile(vv, x, mnm)
if x == 1 then mnm = vv end
end
end
args[1] = vv
args[2] = nm
args[3] = ds
args[4] = op
args[5] = p4
args[6] = p5
args.l = il
args.lang = lg
args.p = px
args.by = by
args.u = um
insert(results, expand('F', args))
if tx ~= '' then
insert(results, expand1('=', tx))
end
end
end
end -- for
if max - cor > 9 then
insert(results, expand1('Igen/cat', 'Own-based with more than 9 files|' .. max))
end
return concat(results)
end -- function ownbased
-- Horizontal file list for: Template:SVG lang, Template:Lang gallery, and others.
function p.svglang(frame)
local gpar = frame.args -- Global parameters.
local template = gpar[1] or 'Source thumb' -- 'SVG lang', 'Lang gallery/thumb'
local ppar = frameCurrent:getParent().args
args = { -- Parameters to expand the given template.
trim(ppar.file or ''),
'', -- [2] = lang
p = trim(ppar.p or ''),
}
local results = {}
for _, v in ipairs(ppar) do
local lang = trim(v)
if lang > ' ' then
args[2] = lang
insert(results, trim(expand(template, args)))
insert(results, NL)
end
end
return concat(results)
end -- function svglang
-- Elements count for: Template:SVG lang, Template:Lang gallery, and others.
function p.elemct (frame)
local ppar = frameCurrent:getParent().args
local count = 0
for _, v in ipairs(ppar) do
if trim(v) ~= '' 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 gpar = frame.args -- Global parameters.
local ppar = frameCurrent:getParent().args
local spa = trim(ppar.spa or ppar.s or '-')
local pfx = trim(ppar.x or ppar.pfx or ppar.prefix or '') -- "List" item {{Comma}}
local dir = gpar[1] or 'none'
if dir == 'vert' and pfx == '' then
pfx = '\n* ' -- "list" item
end
local nam = trim(ppar.n or ppar.name or '')
local dis = trim(ppar.d or ppar.z or ppar.dis or ppar.display or '')
if sub(dis, -2) == 'px' then dis = sub(dis, 1, -3) end
local opt = trim(ppar.o or ppar.opt or ppar.option or '')
local pr4 = trim(ppar.par4 or ppar.qpar or '')
local pr5 = trim(ppar.par5 or ppar.rpar or '')
local pr6 = trim(ppar.par6 or ppar.vpar or '')
local wik = trim(ppar.w or ppar.k or ppar.wiki or ppar.sisterproject or '')
local int = trim(ppar.i or ppar.int or ppar.ind or ppar.inter or '')
local pre = trim(ppar.p or ppar.pre or ppar.pretext or '')
local by0 = trim(ppar.user or ppar.by or '')
local mod = trim(ppar.m or ppar.mod or '')
local lnk = trim(ppar.l or ppar.lnk or ppar.link or '')
local con = trim(ppar.conj or ppar.con or ppar.c or '')
con = sub(con, 1, 1)
if ppar.z then
if con == '' then con = 'n' end
if opt == '' then opt = 'Z' end
if pre == '' then pre = ' ' end
end
local vary = trim(ppar.vary or ppar.v or '') -- Variable pattern
local var1 = trim(ppar.var1 or ppar.v1 or '') -- Variable filling space before
local var2 = trim(ppar.var2 or ppar.v2 or '') -- Variable filling space after
local replacement = {
(var1 == 'space') and ' ' or var1,
'', -- [2] = (sp == '+' and ' ' or '')
'', -- [3] = vv
(var2 == 'space') and ' ' or var2,
}
local stab = {}
local xtab = {}
local ntab = {}
local dtab = {}
local otab = {}
local qtab = {}
local rtab = {}
local vtab = {}
local ktab = {}
local itab = {}
local ptab = {}
local btab = {}
local utab = {}
local ltab = {}
local ttab = {}
local loop = {}
local mnum = 0 -- maximum index reached in the previous tables of indexed parameters
for i, v in ipairs(ppar) do
local z = tostring(i)
stab[i] = trim(ppar['s' .. z] or '~')
xtab[i] = trim(ppar['x' .. z] or '-')
ntab[i] = trim(ppar['n' .. z] or ppar['l' .. z] or '-')
dtab[i] = trim(ppar['d' .. z] or '-')
otab[i] = trim(ppar['o' .. z] or '-')
qtab[i] = trim(ppar['q' .. z] or '-')
rtab[i] = trim(ppar['r' .. z] or '-')
vtab[i] = trim(ppar['v' .. z] or '-')
ktab[i] = trim(ppar['k' .. z] or '-')
itab[i] = trim(ppar['i' .. z] or '-')
ptab[i] = trim(ppar['p' .. z] or '-')
btab[i] = trim(ppar['b' .. z] or ppar['by' .. z] or '-')
utab[i] = trim(ppar['m' .. z] or '-')
ltab[i] = trim(ppar['l' .. z] or '-')
ttab[i] = trim(ppar['t' .. z] or '-')
mnum = i
end
local args = { -- Parameters for expanding 'Template:F'.
'', -- [1] = vv
'', -- [2] = nm
'', -- [3] = ds
'', -- [4] = op
'', -- [5] = p4
'', -- [6] = p5
'', -- [7] = p6
l = '', -- ['l'] = pk
lang = '', -- ['lang'] = pi
p = '', -- ['p'] = pr
by = '', -- ['by'] = by
u = '', -- ['u'] = um
link = '', -- ['link'] = (ln ~= '' and ln or nil)
}
local results = {}
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 = trim(ppar[i])
if vary ~= '' and sub(vv, 1, 1) == '#' and sub(vv, -1) == '#' then -- Loop processing?
loop = split(vv, '#', true--[[plain]])
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 sub(vv, 1, 1) == '"' and sub(vv, -1) == '"' then -- Text processing?
insert(results, expand1('=', sub(vv, 2, -2)))
elseif vary == '' or i > 1 then
local sp = stab[i] ~= '~' and stab[i] or spa
local px = xtab[i] ~= '-' and xtab[i] or pfx
local nm = ntab[i] ~= '-' and ntab[i] or nam
local ds = dtab[i] ~= '-' and dtab[i] or dis
local op = otab[i] ~= '-' and otab[i] or opt
local p4 = qtab[i] ~= '-' and qtab[i] or pr4
local p5 = rtab[i] ~= '-' and rtab[i] or pr5
local p6 = vtab[i] ~= '-' and vtab[i] or pr6
local pk = ktab[i] ~= '-' and ktab[i] or wik
local pi = itab[i] ~= '-' and itab[i] or int
local pr = ptab[i] ~= '-' and ptab[i] or pre
local by = btab[i] ~= '-' and btab[i] or by0
local um = utab[i] ~= '-' and utab[i] or mod
local ln = ltab[i] ~= '-' and ltab[i] or lnk
local tx = ttab[i] ~= '-' and ttab[i] or ''
if vary ~= '' then
replacement[2] = (sp == '+' and ' ' or '')
replacement[3] = vv
vv = gsub(ppar[1], vary, concat(replacement))
else
vv = samefile(vv, i, ppar[1])
end
if vv ~= '' and vv ~= '×' then
if dir == 'hori' and con ~= 'n' then -- Horizontal list.
px = (i == 1) and ' ' or commaAnd(i == mnum)
else -- elseif dir == 'vert' then -- Vertical list.
px = (i == 1) and ' ' or px
end
if px ~= '' then insert(results, px) end
if by == '' then -- Check for abbreviating '/'.
local sby = find(vv, '/')
if sby ~= nil then
by = sub(vv, sby + 1)
vv = sub(vv, 1, sby - 1)
end
end
args[1] = vv
args[2] = nm
args[3] = ds
args[4] = op
args[5] = p4
args[6] = p5
args[7] = p6
args.l = pk
args.lang = pi
args.p = pr
args.by = by
args.u = um
args.link = (ln ~= '' and ln or nil)
insert(results, expand('F', args))
if tx ~= '' then insert(results, expand1('=', tx)) end
end -- if vary
end -- if vv
i = i + j -- next in do loop
end -- while
return concat(results)
end -- function filelist
-- For Template:Attribs (param pairs; but also for single params).
function p.attribs(frame)
local ppar = frameCurrent:getParent().args
local un = trim(ppar.by or ppar.U or ppar.u or '')
local md = trim(ppar.m or ppar.mod or '')
local tt = trim(ppar.t or ppar.to or '') -- "to" topic.
local f = trim(ppar.f or ppar.from or tt) -- "from".
local p = trim(ppar.p or ppar.part or '')
local args = { -- Parameters for expandnig 'Template:Attrib'.
'', -- [1] = vx
'', -- [2] = ux
'-',
trim(ppar.type or 'SVG'), -- Needs check.
'',
ux, -- [6] = ux
tt,
'', -- [8] = (ftab[hnum] ~= '.') and ftab[hnum] or f
'', -- [9] = (ptab[hnum] ~= '.') and ptab[hnum] or p
m = '', -- ['m'] = (mtab[hnum] ~= '.') and mtab[hnum] or md
s = trim(ppar.s or ppar.style or 's'), -- default
}
local ftab = {} -- "from" topic.
local ptab = {} -- Parts.
local mtab = {} -- Modification.
local rtab = {} -- Working table.
local fnum = 0
local rnum = 0
local hnum = 0
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 = trim(v)
-- A rough check: is_extension?
local snam = lower(sub(enam, -4))
if snam == '.png'
or snam == '.gif'
or snam == '.jpg'
or snam == '.svg'
-- Exotics:
or snam == '.mid'
or snam == '.ogg'
or snam == '.tif'
or snam == '.wav'
or snam == '.xcf'
or snam == '.pdf' -- Note: no "short alias" in samefile()
-- Note: Longer extensions still not supported (need check):
-- snam == '.djvu'
-- snam == '.webp'
then -- No - it's the next filename.
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) / 2
local z = tostring(hnum)
ftab[hnum] = ppar['f' .. z] or '.'
ptab[hnum] = ppar['p' .. z] or '.'
mtab[hnum] = ppar['m' .. z] or '.'
end
-- table.maxnum(ppar) does not work; therefore the "rtab" workaround.
insert(rtab, ppar[i]) -- = enam
rnum = rnum + 1
end
if rnum % 2 == 1 then -- Plus one item when odd number.
insert(rtab, '') -- Empty user name, to get a pair.
end
local results = {} -- Output.
local vx = ''
for i, v in ipairs(rtab) do
if i % 2 == 1 then -- odd: 1st value in pair is a filename
vx = trim(v) -- this 1st 'v' should not be empty
else -- even: 2nd value in pair is a user name (possibly empty)
local ux = trim(v) -- this 2nd 'v' can be empty
if ux == '' then ux = un end -- Does not work otherwise?
hnum = i / 2
args[1] = vx -- a filename
args[2] = ux -- a username
args[6] = ux -- a username
args[8] = (ftab[hnum] ~= '.') and ftab[hnum] or f
args[9] = (ptab[hnum] ~= '.') and ptab[hnum] or p
args.m = (mtab[hnum] ~= '.') and mtab[hnum] or md
insert(results, expand('Attrib', args))
vx = '' -- reset the filename for the next pairs
end
end
return concat(results)
end -- function attribs
-- Get the user id: the (last) parameter which is prefixed by '/'.
function p.byuser(frame)
local ppar = frameCurrent:getParent().args
local user = ''
for _, value in pairs(ppar) do
if value ~= nil and user == '' and sub(value, 1, 1) == '/' then
user = sub(value, 2) -- remove the '/'
end
-- TODO: Test whether userID exists?
end
return user
end -- function byuser, for template:F
-- for Template:userlist (horizontal - but vertical when dir=I/O/U/D/V).
function p.userlist(frame)
local gpar = frame.args -- global parms (par/P, dir/V)
local ppar = frameCurrent:getParent().args
local mod = ppar.m or ppar.mod or ppar.user or ppar.u or ppar.page or ppar.p or ''
local hil = ppar.h or ppar.hilite or ''
local nam = ppar.n or ppar.name or ''
local wik = ppar.w or ppar.wiki or ppar.lang or ''
local opt = ppar.o or ppar.opt or ppar.option or ''
local lnk = ppar.l or ppar.link or ''
--@ local pr4 = ppar.q or ppar.qpar or ppar.par4 or ''
--@ local pr5 = ppar.r or ppar.rpar or ppar.par5 or ''
local cas = ppar.c or ppar.case or ''
local trl = ppar.t or ppar.i18n or ppar.translate or ''
-- If ever template expansion of prim, prfx, sufx, or pend is needed,
-- this must be done once here (out of the loop below).
local prim, prfx, sufx, pend = '', '', '', ''
if gpar.dir == 'O' then -- Ordered/numbered vertical list.
prim, prfx, sufx, pend = '<ol>', '<li>', '</li>', '</ol>'
elseif gpar.dir == 'U' then -- Unordered/bulleted vertical list.
prim, prfx, sufx, pend = '<ul>', '<li>', '</li>', '</ul>'
elseif gpar.dir == 'D' then -- Simple definition/indented vertical list.
prim, prfx, sufx, pend = '<dl>', '<dt>', '</dt>', '</dl>'
elseif gpar.dir == 'V' then -- Custom vertical list of users.
prim, prfx, sufx, pend = '<dl style="margin:.3em 0">', '<dt><span style="color:#69F">✦ </span>', '</dt>', '</dl>'
else --'I': Inline horizontal list.
-- prim, prfx, sufx, pend: not used; replaced by separators, expanded by commaAnd()
end
local mtab = {} -- u_mod c/t/w/wt (aka utab, mtab, ptab)
local htab = {} -- hilite
local ntab = {} -- 2 display name
local wtab = {} -- 3 interwiki
local otab = {} -- 4 +/- option
local ltab = {} -- link option
--@ local qtab = {}
--@ local rtab = {}
local ctab = {} -- case
local ttab = {} -- translate i18n
local xtab = {} -- postfix text
local x = 0 -- running index, can be ~= i
local y = 0 -- running index, can be ~= i
local plus = '' -- opt
local xmax = 0
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 --[[1=user, 0=name]] then
x = x
else
x = x + 1
local z = tostring(x)
mtab[x] = ppar['m' .. z] or ppar['u' .. z] or ppar['p' .. z] or '-'
htab[x] = ppar['h' .. z] or '-'
ntab[x] = ppar['n' .. z] or '-'
wtab[x] = ppar['w' .. z] or '-'
otab[x] = ppar['o' .. z] or '/'
ltab[x] = ppar['l' .. z] or '-'
--@ qtab[x] = ppar['q' .. z] or '-'
--@ rtab[x] = ppar['r' .. z] or '-'
ctab[x] = ppar['c' .. z] or '-'
ttab[x] = ppar['t' .. z] or '-'
xtab[x] = ppar['x' .. z] or '-'
end
end
xmax = x
end -- for
if plus ~= '' and opt == '' then
opt = plus --[[discrepancy? opt has priority]]
end
local odd = y % 2 --[[1 when last one not paired]]
local p2s = {
'~',
'', -- [2] = (wk ~= '') and wk or 'commons'
'wiki',
}
local hls = {
'<',
'', -- [2] = hl
'>',
'', -- [4] = (nm ~= '') and nm or vv
'</',
'', -- [6] = hl
'>',
}
local args = { -- Parameters for expanding 'Tempplate:U/main'.
'', -- [1] = vv,
'', -- [2] = nm,
'', -- [3] = wk,
'', -- [4] = op,
link = '', -- ['link'] = lk
par1 = '', -- ['par1'] = p1
par2 = '', -- ['par2'] = p2
--@ par4 = '', -- ['par4'] = p4
--@ par5 = '', -- ['par5'] = p5
case = '', -- ['case'] = cs
i18n = '', -- ['i18n'] = tr
}
local results = {}
local out = 0
local vv = ''
x = 0
y = 0
for _, v in ipairs(ppar) do
v = 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 md = (mtab[x] ~= '-') and mtab[x] or mod
local hl = (htab[x] ~= '-') and htab[x] or hil
local nm = (ntab[x] ~= '-') and ntab[x] or nam
local wk = (wtab[x] ~= '-') and wtab[x] or wik
local op = (otab[x] ~= '/') and otab[x] or opt
local lk = (ltab[x] ~= '-') and ltab[x] or lnk
--@ local p4 = (qtab[x] ~= '-') and qtab[x] or pr4
--@ local p5 = (rtab[x] ~= '-') and rtab[x] or pr5
local cs = (ctab[x] ~= '-') and ctab[x] or cas
local tr = (ttab[x] ~= '-') and ttab[x] or trl
local tx = (xtab[x] ~= '-') and xtab[x] or ''
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
hls[2] = hl
hls[4] = (nm ~= '') and nm or vv
hls[6] = hl
nm = concat(hls)
end
if md == 'n' or md == 'no' then
lk = md -- no link
end
local p1 = '' -- prefix
if md == 't' or md == 'wt' or md == 'tw' then
p1 = ' talk'
end
local p2 = '' -- postfix
if md == 'w' or md == 'wt' or md == 'tw' then
p2s[2] = (wk ~= '') and wk or 'commons'
p2 = concat(p2s)
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 (uses: prim, prfx, sufx, pend).
if out == 0 then
insert(results, prim) -- expansion of prim not needed
end
insert(results, prfx) -- expansion of prefx not needed
out = out + 1
else -- Horizontal inline list.
if x ~= 1 then
insert(results, commaAnd(x == xmax))
end
end
if sub(vv, 1, 2) == '{{' or sub(vv, 1, 2) == '[[' then
insert(results, expand1('=', vv))
else
args[1] = vv
args[2] = nm
args[3] = wk
args[4] = op
args.link = lk
args.par1 = p1
args.par2 = p2
--@ args.par4 = p4
--@ args.par5 = p5
args.case = cs
args.i18n = tr
insert(results, expand('U/main', args))
end
if tx ~= '' then
insert(results, expand1('=', tx))
end
if sufx ~= '' then
insert(results, sufx) -- expansion of sufx not needed
end
end
end
end
end -- for
if out > 0 and pend ~= '' then
insert(results, pend) -- expansion of pend not needed
end
return concat(results)
end -- function userlist
-- Table for templates: Legend; Legend-line, Legend2, Legend-small... (param pairs)
function p.legendt (frame)
local ppar = frameCurrent:getParent().args
local ttip = trim(ppar.tt or ppar.ttip or ppar.tooltipping or '')
local frmp = frame.args
local template = trim(frmp[1] or 'Legend')
local args = { -- Parameters for expanding the given template.
'', -- [1] = v1
'', -- [2] = v
lang = trim(ppar.lang or ''),
p = trim(frmp[2] or ''),
size = trim(frmp[3] or ''),
css = '', -- ['css'] = '" title="' .. upper(v1)
}
local results = {}
local v1 = ''
for _, v in ipairs(ppar) do
if v1 == '' then
v1 = v
if ttip == 'yes' and v1 ~= '' then -- Tooltip the colors.
-- HACK: terminates the style="..." attribute and opens another attribute
args.css = '" title="' .. Ucfirst(v1)
end
else
args[1] = v1
args[2] = v
insert(results, expand(template, args))
v1 = ''
args.css = ''
end
end
if v1 ~= '' then -- Last item: problem when not a last pair
insert(results, expand(template, args))
end
return concat(results)
end -- function legendt
-- For Template:ColorString (Igen/cbox, param pairs).
local argsColorbox = { -- Parameters for expanding 'Template:Igen/cbox'.
'', -- [1] -- (striped; border)
'', -- [2] -- color value
'', -- [3] -- either code-2 ( BCDLMST-), or color value
0, -- [4] -- count of spaces
'', -- [5] -- ?
'', -- [6] -- 'char' or 'file'
'', -- ['lang'] = ppar.lang or '{{PAGELANGUAGE}}',
}
function p.colorbox(frame)
local ppar = frameCurrent:getParent().args
argsColorbox.lang = ppar.lang or '{{PAGELANGUAGE}}'
local results = {}
local v1 = ''
for i, v in ipairs(ppar) do
if i <= 2 then
argsColorbox[3] = v -- either code-2 ( BCDLMST-), or color value
elseif v1 == '' then
v1 = v -- (striped; border)
local ct = 0 -- count of spaces
local bp = 0 -- position of first space
for c = 1, len(v1) do
if sub(v1, c, c) == ' ' then
ct = ct + 1 -- space count
if bp == 0 then bp = c end
end
end
local bc = ''
if ct >= 3 then -- 3 = border, 4 = line
bc = sub(v1, bp + 1)
v1 = sub(v1, 1, bp - 1)
end
local cf = ''
if ct == 1 then -- 'char' or 'file'
bc = sub(v1, bp + 1)
v1 = sub(v1, 1, bp - 1)
cf = (sub(bc, -4, -4 ) == '.') and 'file' or 'char'
end
argsColorbox[4] = ct -- count of spaces
argsColorbox[5] = bc
argsColorbox[6] = cf -- 'char' or 'file'
else
argsColorbox[1] = v1
argsColorbox[2] = v
insert(results, expand('Igen/cbox', argsColorbox))
v1 = ''
end
end
if v1 ~= '' then
argsColorbox[1] = v1
argsColorbox[2] = ''
insert(results, expand('Igen/cbox', argsColorbox)) -- last item
end
return concat(results)
end -- function colorbox
--------------------------------------------------------------------------------
-- Returns a parameter list: (replacement of «#» or «~» by «=»: not necessary).
function p.plist(frame)
local ppar = frameCurrent:getParent().args
return concat(ppar, '|') -- return gsub(concat(ppar, '|'), '#', '='), not necessary
end -- function plist
-- Repeats a text string.
function p.loop (frame)
local ppar = frameCurrent:getParent().args
return string.rep(ppar[2] or ' ', tonumber(ppar[1] or 1))
end -- function loop
-- Increment a hex number by a ±decimal value
function p.incrhx(frame)
local gpar = frame.args -- global parms
return format('%X', tonumber(gpar[1], 16) + gpar[2])
end -- function incrhx
-- Display inline a range of characters with their Unicode code point in tool tip, such as emojis for Template:Emoji, with optional
-- line breaks to limit lines to at most 16 characters (breaks after every code point which is the last one in a UCS column):
-- {{#invoke:Iteration|parlst |temp=Emoji |1=(nb of characters) |2=(4- to 6-digits uppercase hexadecimal code point)
-- |3=(border or 0) |4=(padding or 0) |5=(font size in px or -)
-- |6=(styles or -) |(nocat=1)
-- }}
local argsEmodis = {
-- WARNING: partial HTML (see the template using this function)
-- This function can only be used after an UNTERMINATED opening HTML tag,
-- whose LAST attribute MUST BE ' title=' not followed by quotes.
-- This returned value closes the opening HTML tag of the template
-- The calling template will then append itself the closing HTML tag.
'"U+',
'', -- [2] = format('%04X', code)
'">',
'', -- [4] = char(code)
nil -- [5] = optional line break
}
function p.emodis(frame)
local gpar = frame.args -- global parms
local code = tonumber(gpar[1], 16) + gpar[2]
argsEmodis[2] = format('%04X', code)
argsEmodis[4] = char(code)
argsEmodis[5] = gpar[3] == '16' and (code % 16 == 15 and '<br />') or nil
return concat(argsEmodis)
end -- function emodis
-- For Template:Emoji table: {{#invoke:Iteration|emotab|prefix=1F250|par1=... | ... }}
local argsEmotab = { -- Parameters for expanding 'Template:F'.
'', -- [1] = file
'', -- [2] = 'Z'
'', -- [3] = '49'
'', -- [4] = 'b'
'', -- [5] = 'l'
'', -- [6] = ''
link = '*',
}
function p.emotab(frame)
local gpar = frame.args -- global parms
local codp
codp = trim(gpar.par1 or '') --[['Z' ]] argsEmotab[2] = (codp ~= '') and codp or nil
codp = trim(gpar.par2 or '') --[['49']] argsEmotab[3] = (codp ~= '') and codp or nil
codp = trim(gpar.par3 or '') --[['b' ]] argsEmotab[4] = (codp ~= '') and codp or nil
codp = trim(gpar.par4 or '') --[['l' ]] argsEmotab[5] = (codp ~= '') and codp or nil
codp = trim(gpar.par5 or '') --[['' ]] argsEmotab[6] = (codp ~= '') and codp or nil
codp = trim(gpar.codp or '')
local parts = {
'', -- [1] = v
'', -- [2] = ((file == 'Emojio' or file == 'Fxemoj' or file == 'OpenMo') and upper or lower)(codp)
'.svg',
}
local results = {}
for _, v in ipairs(gpar) do
local file = sub(v, 1, 6)
parts[1] = v
parts[2] = ((file == 'Emojio' or file == 'Fxemoj' or file == 'OpenMo') and upper or lower)(codp)
file = concat(parts)
if titleNew(file, 6).exists then
argsEmotab[1] = file
insert(results, expand('F', argsEmotab))
end
end
return concat(results)
end -- function emotab
-- For template: Navcat (via Navcattab).
function p.navcat(frame)
local gpar = frame.args -- global parms
local cats = { -- Categories to generate.
trim(gpar.c1 or ''),
'', -- [2] = ctxt
trim(gpar.c2 or ''),
}
local args = { -- Parameters for expanding 'Template:Navcat'.
'', -- [1] = (ccod = vtab[2] --[[opt.]])
'', -- [2] = concat(cats)
'', -- [3] = (dtxt = Ucfirst(ctxt))
'', -- [4] = (cnam = vtab[3] or ccod or ctxt)
span = trim(gpar.span or ''),
}
local ppar = frameCurrent:getParent().args
local results = {}
for _, v in ipairs(ppar) do
v = trim(v)
if v == '' or v == 'nl' then
insert(results, '<br /> ')
else
local vtab = split(v, '/')
local ctxt = vtab[1] or ' ' --[[requ.]]
local ccod = vtab[2] --[[opt.]]
cats[2] = ctxt
args[1] = ccod
args[2] = concat(cats)
args[3] = Ucfirst(ctxt)
args[4] = vtab[3] or ccod or ctxt
insert(results, expand('Navcat', args))
end
end
return concat(results)
end -- function navcat
-- For Template:Tle.
function p.tleparm(frame)
local ppar = frameCurrent:getParent().args
local parnumber = trim(ppar.number or '')
local parkbd = trim(ppar.kbd or '')
if parkbd ~= '' then
parkbd = {
'<kbd>',
'', -- [2] = par1
'</kbd>',
}
else
parkbd = nil
end
local args = { -- Parameters for expanding 'Template:Tle/parm'.
'', -- [1] = par1
'', -- [2] = v
trim(ppar.f or ''), -- form feed
trim(ppar.style or ''), -- style for parameter value
trim(ppar.style2 or ''), -- style for parameter name
}
local results = {}
for i, v in ipairs(ppar) do
local z = tostring(i)
local par1 = trim(ppar['p' .. z] or '')
if par1 ~= '' and parkbd then
parkbd[2] = par1
par1 = concat(parkbd)
end
if par1 == '' and parnumber ~= '' then
par1 = z
end
if par1 ~= '' then -- keep parameter value as is if positional (and not parnumberd explicitly)
v = trim(v)
if v == '' then v = ' ' --[[Note: ' ' would change the semantics.]] end
end
args[1] = par1
args[2] = v
insert(results, expand('Tle/parm', args))
end
return concat(results)
end -- function tleparm
-- for Template:Variate.
function p.variation(frame)
local ppar = frameCurrent:getParent().args
local sept = ppar.sept or '|' -- separator before
local sample = trim(ppar[1] or '') -- sample string
local pattern = ppar.pttn or '?' -- pattern
local var1 = ppar.var1 or '' -- filling space before
local var2 = ppar.var2 or '' -- filling space after
local sep2 = ppar.sep2 or '' -- separator after
local parts = { -- Replacement parts for gsub().
(var1 == 'space') and ' ' or var1,
'', -- [2] = trim(v)
(var2 == 'space') and ' ' or var2,
}
local results = {}
for i, v in ipairs(ppar) do
if i > 1 then
insert(results, sept)
parts[2] = trim(v)
insert(results, gsub(sample, pattern, concat(parts)))
insert(results, sep2)
end
end
return concat(results)
end
return p