Module:TemplatePar: Difference between revisions

Content deleted Content added
bugfix (valid unnamed) + kl. updates
update from global upstream version, as requested by the developer; pre-view tested
 
(9 intermediate revisions by 8 users not shown)
Line 1:
--[=[local TemplatePar 2013= { serial = "2023-0903-0620",
suite = "TemplatePar",
item = 15393417,
globals = { DateTime = 20652535,
FileMedia = 24765326,
Multilingual = 47541920,
TemplUtl = 52364930,
URLutil = 10859193 } }
--[=[
Template parameter utility
* assert
Line 6 ⟶ 14:
* countNotEmpty
* downcase()
* duplicates
* match
* valid
* verify()
* TemplatePar()
* failsafe()
]=]
 
 
local Local = { frame = false }
local Failsafe = TemplatePar
local GlobalMod = Local
 
 
 
-- Module globals
Local.messagePrefix = "lua-module-TemplatePar-"
local TemplatePar = { }
Local.L10nDef = {}
local messagePrefix = "lua-module-TemplatePar-"
Local.L10nDef.en = {
local l10nDef = {}
l10nDef.en = {
badPattern = "#invoke:TemplatePar pattern syntax error",
dupOpt = "#invoke:TemplatePar repeated optional parameter",
Line 26 ⟶ 40:
invalidPar = "#invoke:TemplatePar invalid parameter",
minmax = "#invoke:TemplatePar min > max",
missing = "#invoke:TemplatePar missing library",
multiSpell = "Error in template * multiple spelling of parameter",
noErrorCatnoMSGnoCAT = "#invoke:TemplatePar noErrorneither andmessage missingnor category",
noname = "#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 = "#invoke:TemplatePar unknown rule"
}
l10nDefLocal.de patterns = {
badPattern = "#invoke:TemplatePar Syntaxfehler des pattern",
dupOpt = "#invoke:TemplatePar Optionsparameter wiederholt",
dupRule = "#invoke:TemplatePar Konflikt key/pattern",
empty = "Fehler bei Vorlage * Pflichtparameter ohne Wert",
invalid = "Fehler bei Vorlage * Parameter ungültig",
invalidPar = "#invoke:TemplatePar Ungültiger Parameter",
minmax = "#invoke:TemplatePar min > max",
multiSpell = "Fehler bei Vorlage * Mehrere Parameter-Schreibweisen",
noErrorCat = "#invoke:TemplatePar noError und keine Kategorie",
noname = "#invoke:TemplatePar Parameter nicht angegeben",
tooLong = "Fehler bei Vorlage * Parameter zu lang",
tooShort = "Fehler bei Vorlage * Parameter zu kurz",
undefined = "Fehler bei Vorlage * Pflichtparameter fehlt",
unknown = "Fehler bei Vorlage * Parametername unbekannt",
unknownRule = "#invoke:TemplatePar Unbekannte Regel"
}
local Patterns = {
[ "ASCII" ] = "^[ -~]*$",
[ "ASCII+" ] = "^[ -~]+$",
Line 80:
[ "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%+/]+$",
Line 85 ⟶ 87:
[ "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,
local patternCJK = false
["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 containsCJK( saccess, 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:
-- sanalyse -- 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( sanalyse, 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()
 
 
Line 123 ⟶ 276:
-- Return some message string
-- Uses:
-- > Local.messagePrefix
-- > l10nDefLocal.L10nDef
-- mw.language.getContentLanguage()
-- mw.message.new()
local-- c = mw.language.getContentLanguage():getCode()
-- Module:Multilingual
local m = mw.message.new( messagePrefix .. say )
-- Foreign()
-- TemplatePar.framing()
-- Multilingual.tabData()
local m = mw.message.new( Local.messagePrefix .. say )
local r = false
if m:isBlank() then
local l10nc = l10nDef[ c ]mw.language.getContentLanguage():getCode()
if notlocal l10n then= Local.L10nDef[ c ]
if l10n = l10nDef[ "en" ]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
r = l10n[ say ]
else
m:inLanguage( c )
Line 141 ⟶ 309:
end
if not r then
r = string.format( "(((%s)))".., say .. ")))"
end
return r
Line 148 ⟶ 316:
 
 
local function failsafefaculty( storyaccept, scanattempt )
-- Check string as possible boolean
-- Test for match (possibly user-defined with syntax error)
-- Precondition:
-- storyaccept -- string; parameter valuerequirement
-- scan -- string; pattern boolean
-- boolean+
-- attempt -- string; to be tested
-- Postcondition:
-- Return nil, if noterror matchingkeyword, elseor non-nilfalse
-- Uses:
-- mw.ustring.match()Module:TemplUtl
-- Foreign()
return mw.ustring.match( story, scan )
end -- failsafe 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()
 
 
Line 177 ⟶ 372:
if type( options.template ) == "string" then
if #options.template > 0 then
r = r .string.format( "%s (%s)", ..r, options.template .. ")"
end
end
end
if suspect then
r = r .string.format( "%s: %s", ..r, suspect )
end
return r
end -- failure()
 
 
 
local function fair( story, scan )
-- Test for match (possibly user-defined with syntax error)
-- Precondition:
-- story -- string; parameter value
-- scan -- string; pattern
-- Postcondition:
-- Return nil, if not matching, else non-nil
-- Uses:
-- mw.ustring.match()
return mw.ustring.match( story, scan )
end -- fair()
 
 
 
local function familiar( accept, attempt )
-- Check string as possible language name or list
-- Precondition:
-- accept -- string; requirement
-- lang
-- langs
-- langW
-- langsW
-- lang+
-- langs+
-- langW+
-- langsW+
-- attempt -- string; to be tested
-- Postcondition:
-- Return error keyword, or false
-- Uses:
-- Module:Multilingual
-- Foreign()
-- Multilingual.isLang()
local r
if attempt and attempt ~= "" then
local MultiL = Foreign( "Multilingual" )
if MultiL and type( MultiL.isLang ) == "function" then
local lazy = accept:find( "W", 1, true )
if accept:find( "s", 1, true ) then
local group = mw.text.split( attempt, "%s+" )
r = false
for i = 1, #group do
if not MultiL.isLang( group[ i ], lazy ) then
r = "invalid"
break -- for i
end
end -- for i
elseif MultiL.isLang( attempt, lazy ) then
r = false
else
r = "invalid"
end
else
r = "missing"
end
elseif accept:find( "+", 1, true ) then
r = "empty"
else
r = false
end
return r
end -- familiar()
 
 
 
local function far( accept, attempt )
-- Check string as possible URL
-- Precondition:
-- accept -- string; requirement
-- url
-- url+
-- attempt -- string; to be tested
-- Postcondition:
-- Return error keyword, or false
-- Uses:
-- Module:URLutil
-- Foreign()
-- URLutil.isWebURL()
local r
if attempt and attempt ~= "" then
local URLutil = Foreign( "URLutil" )
if URLutil and type( URLutil.isWebURL ) == "function" then
if URLutil.isWebURL( attempt ) then
r = false
else
r = "invalid"
end
else
r = "missing"
end
elseif accept:find( "+", 1, true ) then
r = "empty"
else
r = false
end
return r
end -- far()
 
 
 
local function fast( accept, attempt )
-- Check string as possible date or time
-- Precondition:
-- accept -- string; requirement
-- datetime
-- datetime+
-- datetime/y
-- datetime/y+
-- datetime/ym
-- datetime/ym+
-- datetime/ymd
-- datetime/ymd+
-- attempt -- string; to be tested
-- Postcondition:
-- Return error keyword, or false
-- Uses:
-- Module:DateTime
-- Foreign()
-- DateTime.DateTime()
local r
r = mw.text.trim( attempt )
if r == "" then
if accept:find( "+", 1, true ) then
r = "empty"
else
r = false
end
else
local DateTime = Foreign( "DateTime" )
if type( DateTime ) == "table" then
local d = DateTime( attempt )
if type( d ) == "table" then
if accept:find( "/", 1, true ) then
r = "invalid"
if accept:sub( 1, 10 ) == "datetime/y" then
if d.year then
r = false
if accept:sub( 1, 11 ) == "datetime/ym" then
if d.month then
if accept:sub( 1, 12 )
== "datetime/ymd" then
if not d.dom then
r = "invalid"
end
end
else
r = "invalid"
end
end
end
end
else
r = false
end
else
r = "invalid"
end
else
r = "invalid"
end
end
return r
end -- fast()
 
 
Line 204 ⟶ 565:
end
if store then
r = store .string.format( "%s; %s", ..store, s )
else
r = s
Line 226 ⟶ 587:
-- false if valid or no answer permitted
-- Uses:
-- > PatternsLocal.patterns
-- failure()
-- mw.text.trim()
-- failsafefaculty()
-- fast()
-- facility()
-- familiar()
-- far()
-- fair()
-- containsCJK()
local r = false
local s = false
local show = nil
local scan = false
local stuff = mw.text.trim( analyze )
if type( options.pattern ) == "string" then
if options.key then
Line 248 ⟶ 615:
end
if s ~= "*" then
scan = PatternsLocal.patterns[ s ]
end
if type( scan ) == "string" then
if s == "n" or s == "0,0" or s == "0.0" then
if not analyzestuff:match( "[0-9]" ) then and
not stuff:match( "^%s*$" ) then
scan = false
if options.say then
show = string.format( "'&quot;%s&quot;" .., options.say .. "'")
end
if abbr then
Line 269 ⟶ 637:
n = tonumber( n )
if n then
local i = tonumber( analyzestuff )
if i then
if op == "<" then
Line 293 ⟶ 661:
r = "undefined"
end
elseif s:match( "^boolean%+?$" ) then
r = faculty( s, stuff )
n = true
elseif s:match( "^datetime/?y?m?d?%+?$" ) then
r = fast( s, stuff )
n = true
elseif s:match( "^image%+?:?$" ) or
s:match( "^file%+?:?$" ) then
r = facility( s, stuff )
n = true
elseif s:match( "langs?W?%+?" ) then
r = familiar( s, stuff )
n = true
elseif s:match( "url%+?" ) then
r = far( s, stuff )
n = true
end
-- datetime+
-- iso8631+
-- line+
if not n and not r then
r = "unknownRule"
Line 299 ⟶ 686:
if r then
if options.say then
show = "'" .. options.say .string.format( "'&quot;%s&quot; %s", options..say, s )
else
show = s
Line 312 ⟶ 699:
end
if scan then
local legal, got = pcall( failsafefair, analyzestuff, scan )
if legal then
if not got then
if s == "aa" then
got = containsCJK( analyzestuff )
end
if not got then
if options.say then
show = string.format( "'&quot;%s&quot;" .., options.say .. "'")
end
if abbr then
Line 331 ⟶ 718:
else
r = failure( "badPattern",
scan .string.format( "%s *** %s", ..scan, got ),
options )
end
Line 347 ⟶ 734:
-- Postcondition:
-- Return true iff found
local k, v, r
for k, v in pairs( haystack ) do
if k == needle then
returnr = true
end
end -- for k, v
return r or false
end -- fed()
 
Line 368 ⟶ 755:
-- Uses:
-- TemplatePar.downcase()
-- mwTemplatePar.getCurrentFrameframing()
-- frame:getParent()
local g, k, v
Line 375 ⟶ 762:
g = TemplatePar.downcase( options )
else
g = mwTemplatePar.getCurrentFrameframing()
if light then
g = g:getParent()
Line 419 ⟶ 806:
if type( sub ) == "string" then
sub = sub:gsub( "%%!", "|" )
sub = sub :gsub( "%%%(%(", "{{" )
sub = sub :gsub( "%%%)%)", "}}" )
:gsub( "\\n", string.char( 10 ) )
options.pattern = sub
options.key = nil
Line 464 ⟶ 852:
-- submit -- string or false or nil; non-empty error message
-- options -- table or nil; optional details
-- options.noErrorformat
-- options.preview
-- options.cat
-- options.template
Line 470 ⟶ 859:
-- Return string or false
-- Uses:
-- TemplatePar.framing()
-- factory()
local r = false
if submit then
local lazy = false
local learn = false
local show = false
local opt, s
if type( options ) == "table" then
opt = options
show = opt.format
lazy = ( show == "" or show == "0" or show == "-" )
s = opt.preview
if type( s ) == "string" and
s ~= "" and s ~= "0" and s ~= "-" then
local sniffer = "{{REVISIONID}}"
if lazy then
show = ""
lazy = false
end
if TemplatePar.framing():preprocess( sniffer ) == "" then
if s == "1" then
show = "*"
else
show = s
end
learn = true
end
end
else
opt = { }
end
if opt.noErrorlazy then
if not opt.cat then
r = submit .string.format( "%s %s" .. factory( "noErrorCat" ),
submit, factory( "noMSGnoCAT" ) )
end
else
r = submit
end
if r and not lazy then
rlocal = "<span class='error'>" .. r .. "</span>"i
if not show or show == "*" then
local e = mw.html.create( "span" )
:attr( "class", "error" )
:wikitext( "@@@" )
if learn then
local max = 1000000000
local id = math.floor( os.clock() * max )
local sign = string.format( "error_%d", id )
local btn = mw.html.create( "span" )
local top = mw.html.create( "div" )
e:attr( "id", sign )
btn:css( { ["background"] = "#FFFF00",
["border"] = "#FF0000 3px solid",
["font-weight"] = "bold",
["padding"] = "2px",
["text-decoration"] = "none" } )
:wikitext( "&gt;&gt;&gt;" )
sign = string.format( "[[#%s|%s]]",
sign, tostring( btn ) )
top:wikitext( sign, "&#160;", submit )
mw.addWarning( tostring( top ) )
end
show = tostring( e )
end
i = show:find( "@@@", 1, true )
if i then
-- No gsub() since r might contain "%3" (e.g. URL)
r = string.format( "%s%s%s",
show:sub( 1, i - 1 ),
r,
show:sub( i + 3 ) )
else
r = show
end
end
if learn and r then
-- r = fatal( r )
end
s = opt.cat
if type( s ) == "string" then
iflocal not r thenlink
if opt.errNS r = ""then
local ns = mw.title.getCurrentTitle().namespace
local st = type( opt.errNS )
if st == "string" then
local space = string.format( ".*%%s%d%%s.*", ns )
local spaces = string.format( " %s ", opt.errNS )
if spaces:match( space ) then
link = true
end
elseif st == "table" then
for i = 1, #opt.errNS do
if opt.errNS[ i ] == ns then
link = true
break -- for i
end
end -- for i
end
else
link = true
end
if s:find( "@@@" )link then
iflocal type(cats, opt.template ) == "string" theni
if not r s = s:gsub( "@@@", opt.template )then
r = ""
end
if s:find( "@@@" ) then
if type( opt.template ) == "string" then
s = s:gsub( "@@@", opt.template )
end
end
cats = mw.text.split( s, "%s*#%s*" )
for i = 1, #cats do
s = mw.text.trim( cats[ i ] )
if #s > 0 then
r = string.format( "%s[[Category:%s]]", r, s )
end
end -- for i
end
r = r .. "[[Category:" .. s .. "]]"
end
end
Line 539 ⟶ 1,019:
-- failure()
-- fed()
local k, v
local r = false
local lack
for k, v in pairs( got ) do
if notk finder( valid, k== )"" then
lack = true
break -- for k, v
elseif not finder( valid, k ) then
r = fault( r, k )
end
end -- for k, v
if rlack then
r = failure( "unknownunavailable", "'" .. r .. "'"false, options )
elseif r then
r = failure( "unknown",
string.format( "&quot;%s&quot;", r ),
options )
else -- all names valid
local i, s
Line 661 ⟶ 1,148:
 
 
local function form( light, options, frame )
-- Run parameter analysis on current environment
-- Precondition:
Line 668 ⟶ 1,155:
-- options.mandatory
-- options.optional
-- frame -- object; #invoke environment, or false
-- Postcondition:
-- Return string with error message as configured;
-- false if valid
-- Uses:
-- TemplatePar.framing()
-- fold()
-- fetch()
Line 677 ⟶ 1,166:
-- finalize()
local duty, r
if frame then
TemplatePar.framing( frame )
end
if type( options ) == "table" then
if type( options.mandatory ) ~= "table" then
Line 718 ⟶ 1,210:
-- feasible()
-- failure()
local r = feasible( analyze, options, false )
local show
if options.min and not r then
Line 725 ⟶ 1,217:
if options.max < options.min then
r = failure( "minmax",
tostringstring.format( options.min"%d > )%d",
.. " > " options..min,
tostring( options.max ),
options )
end
Line 734 ⟶ 1,226:
show = " <" .. options.min
if options.say then
show = show .string.format( "%s '&quot;%s&quot;", ..show, options.say .. "'")
end
r = failure( "tooShort", show, options )
Line 747 ⟶ 1,239:
show = " >" .. options.max
if options.say then
show = show .string.format( "%s '&quot;%s&quot;", ..show, options.say .. "'")
end
r = failure( "tooLong", show, options )
Line 770 ⟶ 1,262:
-- false if valid or no answer permitted
-- Uses:
-- mw.text.trim()
-- format()
-- failure()
local r = false
if type( assignment ) == "table" then
local story = assignment.args[ access ] or ""
if type( storyaccess ) == "stringnumber" then
ifstory type= mw.text.trim( optionsstory ) ~= "table" then
options = { }end
if type( options ) end~= "table" then
options.say = access{ }
r = format( story, options )
else
r = failure( "invalid", access, options )
end
options.say = access
r = format( story, options )
end
return r
Line 799 ⟶ 1,291:
-- Uses:
-- form()
-- mw.text.trim()
-- failure()
-- TemplatePar.assertfinalize()
-- TemplatePar.valid()
-- TemplatePar.assert()
local options = { mandatory = { "1" },
optional = { "2",
"cat",
"errNS",
"low",
"max",
"min",
"noErrorformat",
"preview",
"template" },
template = string.format( "&#35;invoke:TemplatePar%s|".. action .. "%s|",
"TemplatePar",
action )
}
local r = form( false, options, frame )
if not r then
local s
options = { cat = frame.args.cat,
errNS = frame.args.errNS,
low = frame.args.low,
noErrorformat = frame.args.noErrorformat,
preview = frame.args.preview,
template = frame.args.template
}
Line 878 ⟶ 1,376:
if ( type( append ) == "string" ) then
if ( append ~= "" ) then
r = append .string.format( "%s<br /> %s", ..append, r )
end
else
Line 900 ⟶ 1,398:
-- Uses:
-- form()
return form( true, options, false )
end -- TemplatePar.check()
 
Line 973 ⟶ 1,471:
-- mw.text.trim()
-- TemplatePar.downcase()
-- mwTemplatePar.getCurrentFrameframing()
-- frame:getParent()
-- formatted()
Line 997 ⟶ 1,495:
params = TemplatePar.downcase( options )
else
params = mwTemplatePar.getCurrentFrameframing():getParent()
end
r = formatted( params, access, options )
Line 1,017 ⟶ 1,515:
-- Uses:
-- form()
return form( false, options, false )
end -- TemplatePar.verify()
 
 
 
TemplatePar.framing = function( frame )
-- Ensure availability of frame object
-- Precondition:
-- frame -- object; #invoke environment, or false
-- Postcondition:
-- Return frame object
-- Uses:
-- >< Local.frame
if not Local.frame then
if type( frame ) == "table" and
type( frame.args ) == "table" and
type( frame.getParent ) == "function" and
type( frame:getParent() ) == "table" and
type( frame:getParent().getParent ) == "function" and
type( frame:getParent():getParent() ) == "nil" then
Local.frame = frame
else
Local.frame = mw.getCurrentFrame()
end
end
return Local.frame
end -- TemplatePar.framing()
 
 
 
Failsafe.failsafe = function ( atleast )
-- Retrieve versioning and check for compliance
-- Precondition:
-- atleast -- string, with required version
-- or wikidata|item|~|@ or false
-- Postcondition:
-- Returns string -- with queried version/item, also if problem
-- false -- if appropriate
-- 2020-08-17
local since = atleast
local last = ( since == "~" )
local linked = ( since == "@" )
local link = ( since == "item" )
local r
if last or link or linked or since == "wikidata" then
local item = Failsafe.item
since = false
if type( item ) == "number" and item > 0 then
local suited = string.format( "Q%d", item )
if link then
r = suited
else
local entity = mw.wikibase.getEntity( suited )
if type( entity ) == "table" then
local seek = Failsafe.serialProperty or "P348"
local vsn = entity:formatPropertyValues( seek )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false
elseif linked then
if mw.title.getCurrentTitle().prefixedText
== mw.wikibase.getSitelink( suited ) then
r = false
else
r = suited
end
else
r = vsn.value
end
end
end
end
end
end
if type( r ) == "nil" then
if not since or since <= Failsafe.serial then
r = Failsafe.serial
else
r = false
end
end
return r
end -- Failsafe.failsafe()
 
 
Line 1,036 ⟶ 1,617:
-- furnish()
return furnish( frame, "assert" )
end -- p.assert()
 
 
Line 1,052 ⟶ 1,633:
"opt",
"cat",
"errNS",
"low",
"noErrorformat",
"preview",
"template" },
template = "&#35;invoke:TemplatePar|check|"
}
local r = form( false, options, frame )
if not r then
options = { mandatory = fill( frame.args.all ),
optional = fill( frame.args.opt ),
cat = frame.args.cat,
errNS = frame.args.errNS,
low = frame.args.low,
noErrorformat = frame.args.noErrorformat,
preview = frame.args.preview,
template = frame.args.template
}
r = form( true, options, frame )
end
return r or ""
end -- p.check()
 
 
Line 1,080 ⟶ 1,665:
-- TemplatePar.count()
return tostring( TemplatePar.count() )
end -- p.count()
 
 
Line 1,091 ⟶ 1,676:
-- TemplatePar.countNotEmpty()
return tostring( TemplatePar.countNotEmpty() )
end -- p.countNotEmpty()
 
 
Line 1,097 ⟶ 1,682:
function p.match( frame )
-- Combined analysis of parameters and their values
-- Precondition:
-- frame -- object; #invoke environment
-- Postcondition:
-- Return string with error message or ""
-- Uses:
-- TemplatePar.framing()
-- mw.text.trim()
-- mw.ustring.lower()
Line 1,110 ⟶ 1,698:
-- finalize()
local r = false
local options = { cat = frame.args.cat,
low errNS = frame.args.lowerrNS,
noErrorlow = frame.args.noErrorlow,
templateformat = frame.args.templateformat,
preview = frame.args.preview,
template = frame.args.template
}
local k, v, s
local params = { }
TemplatePar.framing( frame )
for k, v in pairs( frame.args ) do
if type( k ) == "number" then
Line 1,148 ⟶ 1,739:
end -- for k, v
options.optional = s
r = form( true, options, frame )
end
if not r then
Line 1,163 ⟶ 1,754:
for k, v in pairs( params ) do
options.say = k
errValues = falsetargs[ k ]
s = targs[ k ]
if s then
if s == "" then
Line 1,181 ⟶ 1,771:
if lack then
if errMiss then
errMisss = errMiss .. ", '" .. k= .."%s, "'&quot;%s&quot;"
errMiss = string.format( s, errMiss, k )
else
errMiss = "'" .. k .string.format( "'&quot;%s&quot;",
k )
end
elseif not errMiss then
Line 1,203 ⟶ 1,795:
end
return r or ""
end -- p.match()
 
 
Line 1,216 ⟶ 1,808:
-- furnish()
return furnish( frame, "valid" )
end -- p.valid()
 
 
 
p.failsafe = function ( frame )
-- Versioning interface
local s = type( frame )
local since
if s == "table" then
since = frame.args[ 1 ]
elseif s == "string" then
since = frame
end
if since then
since = mw.text.trim( since )
if since == "" then
since = false
end
end
return Failsafe.failsafe( since ) or ""
end -- p.failsafe
 
 
Line 1,225 ⟶ 1,837:
-- Return table with functions
return TemplatePar
end -- p.TemplatePar()
 
 
 
setmetatable( p, { __call = function ( func, ... )
setmetatable( p, nil )
return Failsafe
end } )
 
return p