local TemplatePar = { serial = "2023-03-20",
suite = "TemplatePar",
item = 15393417,
globals = { DateTime = 20652535,
FileMedia = 24765326,
Multilingual = 47541920,
TemplUtl = 52364930,
URLutil = 10859193 } }
--[=[
Template parameter utility
- assert
- check
- count
- countNotEmpty
- downcase
- duplicates
- match
- valid
- verify
- TemplatePar
- failsafe
]=]  
local Local = { = false }
local Failsafe = TemplatePar
local GlobalMod = Local  
-- Module globals
Local.messagePrefix = "lua-module-TemplatePar-"
Local.L10nDef = {}
Local.L10nDef.en = {
badPattern = "&#35;invoke:TemplatePar pattern syntax error",
dupOpt = "&#35;invoke:TemplatePar repeated optional parameter",
dupRule = "&#35;invoke:TemplatePar conflict key/pattern",
empty = "Error in template * undefined value for mandatory",
invalid = "Error in template * invalid parameter",
invalidPar = "&#35;invoke:TemplatePar invalid parameter",
minmax = "&#35;invoke:TemplatePar min > max",
missing = "&#35;invoke:TemplatePar missing library",
multiSpell = "Error in template * multiple spelling of parameter",
noMSGnoCAT = "&#35;invoke:TemplatePar neither message nor category",
noname = "&#35;invoke:TemplatePar missing parameter name",
notFound = "Error in template * missing page",
tooLong = "Error in template * parameter too long",
tooShort = "Error in template * parameter too short",
unavailable = "Error in template * parameter name missing",
undefined = "Error in template * mandatory parameter missing",
unknown = "Error in template * unknown parameter name",
unknownRule = "&#35;invoke:TemplatePar unknown rule"
}
Local.patterns = {
[ "ASCII" ] = "^[ -~]*$",
[ "ASCII+" ] = "^[ -~]+$",
[ "ASCII+1" ] = "^[!-~]+$",
[ "n" ] = "^[%-]?[0-9]*$",
[ "n>0" ] = "^[0-9]*[1-9][0-9]*$",
[ "N+" ] = "^[%-]?[1-9][0-9]*$",
[ "N>0" ] = "^[1-9][0-9]*$",
[ "x" ] = "^[0-9A-Fa-f]*$",
[ "x+" ] = "^[0-9A-Fa-f]+$",
[ "X" ] = "^[0-9A-F]*$",
[ "X+" ] = "^[0-9A-F]+$",
[ "0,0" ] = "^[%-]?[0-9]*,?[0-9]*$",
[ "0,0+" ] = "^[%-]?[0-9]+,[0-9]+$",
[ "0,0+?" ] = "^[%-]?[0-9]+,?[0-9]*$",
[ "0.0" ] = "^[%-]?[0-9]*[%.]?[0-9]*$",
[ "0.0+" ] = "^[%-]?[0-9]+%.[0-9]+$",
[ "0.0+?" ] = "^[%-]?[0-9]+[%.]?[0-9]*$",
[ ".0+" ] = "^[%-]?[0-9]*[%.]?[0-9]+$",
[ "ID" ] = "^[A-Za-z]?[A-Za-z_0-9]*$",
[ "ID+" ] = "^[A-Za-z][A-Za-z_0-9]*$",
[ "ABC" ] = "^[A-Z]*$",
[ "ABC+" ] = "^[A-Z]+$",
[ "Abc" ] = "^[A-Z]*[a-z]*$",
[ "Abc+" ] = "^[A-Z][a-z]+$",
[ "abc" ] = "^[a-z]*$",
[ "abc+" ] = "^[a-z]+$",
[ "aBc+" ] = "^[a-z]+[A-Z][A-Za-z]*$",
[ "w" ] = "^%S*$",
[ "w+" ] = "^%S+$",
[ "base64" ] = "^[A-Za-z0-9%+/]*$",
[ "base64+" ] = "^[A-Za-z0-9%+/]+$",
[ "aa" ] = "[%a%a].*[%a%a]",
[ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%c-%c%c]+$",
1, 31, 127 ),
[ "ref" ] = string.format( "%c'%c`UNIQ%s%sref%s%s%sQINU`%c'%c",
127, 34, "%-", "%-", "%-", "%x+",
"%-", 34, 127 ),
[ "+" ] = "%S"
}
Local.boolean = { ["1"] = true,
["true"] = true,
y = true,
yes = true,
on = true,
["0"] = true,
["false"] = true,
["-"] = true,
n = true,
no = true,
off = true }
Local.patternCJK = false  
local foreignModule = function ( access, advanced, append, alt, alert )
-- Fetch global module
-- Precondition:
-- access -- string, with name of base module
-- advanced -- true, for require; else mw.loadData
-- append -- string, with subpage part, if any; or false
-- alt -- number, of wikidata item of root; or false
-- alert -- true, for throwing error on data problem
-- Postcondition:
-- Returns whatever, probably table
-- 2020-01-01
local storage = access
local finer = function
if append then
storage = string.format( "%s/%s",
storage,
append )
end
end
local fun, lucky, r, suited
if advanced then
fun = require
else
fun = mw.loadData
end
GlobalMod.globalModules = GlobalMod.globalModules or { }
suited = GlobalMod.globalModules[ access ]
if not suited then
finer
lucky, r = pcall( fun, "Module:" .. storage )
end
if not lucky then
if not suited and
type( alt ) == "number" and
alt > 0 then
suited = string.format( "Q%d", alt )
suited = mw.wikibase.getSitelink( suited )
GlobalMod.globalModules[ access ] = suited or true
end
if type( suited ) == "string" then
storage = suited
finer
lucky, r = pcall( fun, storage )
end
if not lucky and alert then
error( "Missing or invalid page: " .. storage )
end
end
return r
end -- foreignModule  
local function Foreign( access )
-- Access standardized library
-- Precondition:
-- access -- string, with name of base module
-- Postcondition:
-- Return library table, or not
-- Uses:
local r
if Local[ access ] then
r = Local[ access ]
else
local bib = foreignModule( access,
true,
false,
TemplatePar.globals[ access ],
false )
if type( bib ) == "table" and
type( bib[ access ] ) == "function" then
bib = bib[ access ]
if type( bib ) == "table" then
r = bib
Local[ access ] = bib
end
end
end
return r
end -- Foreign  
local function containsCJK( analyse )
-- Is any CJK character present?
-- Precondition:
-- analyse -- string
-- Postcondition:
-- Return false iff no CJK present
-- Uses:
-- >< Local.patternCJK
-- mw.ustring.char
-- mw.ustring.match
local r = false
if not Local.patternCJK then
Local.patternCJK = mw.ustring.char( 91,
13312, 45, 40959,
131072, 45, 178207,
93 )
end
if mw.ustring.match( analyse, Local.patternCJK ) then
r = true
end
return r
end -- containsCJK  
local function facility( accept, attempt )
-- Check string as possible file name or other source page
-- Precondition:
-- accept -- string; requirement
-- file
-- file+
-- file:
-- file:+
-- image
-- image+
-- image:
-- image:+
-- attempt -- string; to be tested
-- Postcondition:
-- Return error keyword, or false
-- Uses:
-- Module:FileMedia
-- Foreign
-- FileMedia.isFile
-- FileMedia.isType
local r
if attempt and attempt ~= "" then
local FileMedia = Foreign( "FileMedia" )
if FileMedia and type( FileMedia.isFile ) == "function"
and type( FileMedia.isType ) == "function" then
local s, live = accept:match( "^([a-z]+)(:?)%+?$" )
if live then
if FileMedia.isType( attempt, s ) then
if FileMedia.isFile( attempt ) then
r = false
else
r = "notFound"
end
else
r = "invalid"
end
elseif FileMedia.isType( attempt, s ) then
r = false
else
r = "invalid"
end
else
r = "missing"
end
elseif accept:match( "%+$" ) then
r = "empty"
else
r = false
end
return r
end -- facility  
local function factory( say )
-- Retrieve localized message string in content language
-- Precondition:
-- say -- string; message ID
-- Postcondition:
-- Return some message string
-- Uses:
-- > Local.messagePrefix
-- > Local.L10nDef
-- mw.message.new
-- mw.language.getContentLanguage
-- Module:Multilingual
-- Foreign
-- TemplatePar.framing
-- Multilingual.tabData
local m = mw.message.new( Local.messagePrefix .. say )
local r = false
if m:isBlank then
local c = mw.language.getContentLanguage:getCode
local l10n = Local.L10nDef[ c ]
if l10n then
r = l10n[ say ]
else
local MultiL = Foreign( "Multilingual" )
if MultiL and type( MultiL.tabData ) == "function" then
local lang
r, lang = MultiL.tabData( "I18n/Module:TemplatePar",
say,
false,
TemplatePar.framing )
end
end
if not r then
r = Local.L10nDef.en[ say ]
end
else
m:inLanguage( c )
r = m:plain
end
if not r then
r = string.format( "(((%s)))", say )
end
return r
end -- factory  
local function faculty( accept, attempt )
-- Check string as possible boolean
-- Precondition:
-- accept -- string; requirement
-- boolean
-- boolean+
-- attempt -- string; to be tested
-- Postcondition:
-- Return error keyword, or false
-- Uses:
-- Module:TemplUtl
-- Foreign
-- TemplUtl.faculty
local r
r = mw.text.trim( attempt ):lower
if r == "" then
if accept == "boolean+" then
r = "empty"
else
r = false
end
elseif Local.boolean[ r ] or r:match( "^[01%-]+$" ) then
r = false
else
local TemplUtl = Foreign( "TemplUtl" )
if TemplUtl and type( TemplUtl.faculty ) == "function" then
r = TemplUtl.faculty( r, "-" )
if r == "-" then
r = "invalid"
else
r = false
end
else
r = "invalid"
end
end
return r
end -- faculty  
local function failure( spec, suspect, options )
-- Submit localized error message
-- Precondition:
-- spec -- string; message ID