Jump to content

Module:TemplatePar and Module:TemplatePar/sandbox: Difference between pages

(Difference between pages)
Page 1
Page 2
Content deleted Content added
update from global upstream version, as requested by the developer; pre-view tested
 
mild simplifications, replace quintuple spaces with tabs
 
Line 1: Line 1:
local TemplatePar = { serial = "2023-03-20",
--[=[ TemplatePar 2015-02-14
suite = "TemplatePar",
item = 15393417,
globals = { DateTime = 20652535,
FileMedia = 24765326,
Multilingual = 47541920,
TemplUtl = 52364930,
URLutil = 10859193 } }
--[=[
Template parameter utility
Template parameter utility
* assert
* assert
Line 14: Line 6:
* countNotEmpty
* countNotEmpty
* downcase()
* downcase()
* duplicates
* match
* match
* valid
* valid
* verify()
* verify()
* TemplatePar()
* TemplatePar()
* failsafe()
]=]
]=]


local Local = { frame = false }
local Failsafe = TemplatePar
local GlobalMod = Local






-- Module globals
-- Module globals
local TemplatePar = { }
Local.messagePrefix = "lua-module-TemplatePar-"
local MessagePrefix = "lua-module-TemplatePar-"
Local.L10nDef = {}
Local.L10nDef.en = {
local L10nDef = {}
L10nDef.en = {
badPattern = "#invoke:TemplatePar pattern syntax error",
dupOpt = "#invoke:TemplatePar repeated optional parameter",
badPattern = "#invoke:TemplatePar pattern syntax error",
dupRule = "#invoke:TemplatePar conflict key/pattern",
dupOpt = "#invoke:TemplatePar repeated optional parameter",
dupRule = "#invoke:TemplatePar conflict key/pattern",
empty = "Error in template * undefined value for mandatory",
invalid = "Error in template * invalid parameter",
empty = "Error in template * undefined value for mandatory",
invalidPar = "#invoke:TemplatePar invalid parameter",
invalid = "Error in template * invalid parameter",
minmax = "#invoke:TemplatePar min > max",
invalidPar = "#invoke:TemplatePar invalid parameter",
missing = "#invoke:TemplatePar missing library",
minmax = "#invoke:TemplatePar min > max",
missing = "#invoke:TemplatePar missing library",
multiSpell = "Error in template * multiple spelling of parameter",
multiSpell = "Error in template * multiple spelling of parameter",
noMSGnoCAT = "#invoke:TemplatePar neither message nor category",
noname = "#invoke:TemplatePar missing parameter name",
noMSGnoCAT = "#invoke:TemplatePar neither message nor category",
noname = "#invoke:TemplatePar missing parameter name",
notFound = "Error in template * missing page",
tooLong = "Error in template * parameter too long",
notFound = "Error in template * missing page",
tooShort = "Error in template * parameter too short",
tooLong = "Error in template * parameter too long",
unavailable = "Error in template * parameter name missing",
tooShort = "Error in template * parameter too short",
undefined = "Error in template * mandatory parameter missing",
undefined = "Error in template * mandatory parameter missing",
unknown = "Error in template * unknown parameter name",
unknown = "Error in template * unknown parameter name",
unknownRule = "#invoke:TemplatePar unknown rule"
unknownRule = "#invoke:TemplatePar unknown rule"
}
}
Local.patterns = {
L10nDef.de = {
badPattern = "#invoke:TemplatePar Syntaxfehler des pattern",
[ "ASCII" ] = "^[ -~]*$",
dupOpt = "#invoke:TemplatePar Optionsparameter wiederholt",
[ "ASCII+" ] = "^[ -~]+$",
dupRule = "#invoke:TemplatePar Konflikt key/pattern",
[ "ASCII+1" ] = "^[!-~]+$",
empty = "Fehler bei Vorlage * Pflichtparameter ohne Wert",
[ "n" ] = "^[%-]?[0-9]*$",
invalid = "Fehler bei Vorlage * Parameter ungültig",
[ "n>0" ] = "^[0-9]*[1-9][0-9]*$",
invalidPar = "#invoke:TemplatePar Ungültiger Parameter",
[ "N+" ] = "^[%-]?[1-9][0-9]*$",
minmax = "#invoke:TemplatePar min > max",
[ "N>0" ] = "^[1-9][0-9]*$",
multiSpell = "Fehler bei Vorlage * Mehrere Parameter-Schreibweisen",
[ "x" ] = "^[0-9A-Fa-f]*$",
noMSGnoCAT = "#invoke:TemplatePar weder Meldung noch Kategorie",
[ "x+" ] = "^[0-9A-Fa-f]+$",
noname = "#invoke:TemplatePar Parameter nicht angegeben",
[ "X" ] = "^[0-9A-F]*$",
notFound = "Fehler bei Vorlage * Seite fehlt",
[ "X+" ] = "^[0-9A-F]+$",
tooLong = "Fehler bei Vorlage * Parameter zu lang",
[ "0,0" ] = "^[%-]?[0-9]*,?[0-9]*$",
tooShort = "Fehler bei Vorlage * Parameter zu kurz",
[ "0,0+" ] = "^[%-]?[0-9]+,[0-9]+$",
undefined = "Fehler bei Vorlage * Pflichtparameter fehlt",
[ "0,0+?" ] = "^[%-]?[0-9]+,?[0-9]*$",
unknown = "Fehler bei Vorlage * Parametername unbekannt",
[ "0.0" ] = "^[%-]?[0-9]*[%.]?[0-9]*$",
unknownRule = "#invoke:TemplatePar Unbekannte Regel"
[ "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 Patterns = {
Local.boolean = { ["1"] = true,
[ "ASCII" ] = "^[ -~]*$",
["true"] = true,
[ "ASCII+" ] = "^[ -~]+$",
y = true,
[ "ASCII+1" ] = "^[!-~]+$",
yes = true,
[ "n" ] = "^[%-]?[0-9]*$",
on = true,
[ "n>0" ] = "^[0-9]*[1-9][0-9]*$",
["0"] = true,
[ "N+" ] = "^[%-]?[1-9][0-9]*$",
["false"] = true,
[ "N>0" ] = "^[1-9][0-9]*$",
["-"] = true,
[ "x" ] = "^[0-9A-Fa-f]*$",
n = true,
[ "x+" ] = "^[0-9A-Fa-f]+$",
no = true,
[ "X" ] = "^[0-9A-F]*$",
off = true }
[ "X+" ] = "^[0-9A-F]+$",
Local.patternCJK = false
[ "0,0" ] = "^[%-]?[0-9]*,?[0-9]*$",

[ "0,0+" ] = "^[%-]?[0-9]+,[0-9]+$",

[ "0,0+?" ] = "^[%-]?[0-9]+,?[0-9]*$",

[ "0.0" ] = "^[%-]?[0-9]*[%.]?[0-9]*$",
local foreignModule = function ( access, advanced, append, alt, alert )
[ "0.0+" ] = "^[%-]?[0-9]+%.[0-9]+$",
-- Fetch global module
[ "0.0+?" ] = "^[%-]?[0-9]+[%.]?[0-9]*$",
-- Precondition:
[ ".0+" ] = "^[%-]?[0-9]*[%.]?[0-9]+$",
-- access -- string, with name of base module
[ "ID" ] = "^[A-Za-z]?[A-Za-z_0-9]*$",
-- advanced -- true, for require(); else mw.loadData()
[ "ID+" ] = "^[A-Za-z][A-Za-z_0-9]*$",
-- append -- string, with subpage part, if any; or false
[ "ABC" ] = "^[A-Z]*$",
-- alt -- number, of wikidata item of root; or false
[ "ABC+" ] = "^[A-Z]+$",
-- alert -- true, for throwing error on data problem
[ "Abc" ] = "^[A-Z]*[a-z]*$",
-- Postcondition:
[ "Abc+" ] = "^[A-Z][a-z]+$",
-- Returns whatever, probably table
[ "abc" ] = "^[a-z]*$",
-- 2020-01-01
[ "abc+" ] = "^[a-z]+$",
local storage = access
[ "aBc+" ] = "^[a-z]+[A-Z][A-Za-z]*$",
local finer = function ()
[ "w" ] = "^%S*$",
if append then
[ "w+" ] = "^%S+$",
storage = string.format( "%s/%s",
[ "base64" ] = "^[A-Za-z0-9%+/]*$",
storage,
[ "base64+" ] = "^[A-Za-z0-9%+/]+$",
append )
[ "aa" ] = "[%a%a].*[%a%a]",
end
[ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%c-%c%c]+$",
end
1, 31, 127 ),
local fun, lucky, r, suited
[ "+" ] = "%S"
if advanced then
}
fun = require
local patternCJK = false
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 )
local function containsCJK( s )
-- Is any CJK character present?
-- Is any CJK character present?
-- Precondition:
-- Precondition:
-- analyse -- string
-- s -- string
-- Postcondition:
-- Postcondition:
-- Return false iff no CJK present
-- Return false iff no CJK present
-- Uses:
-- Uses:
-- >< Local.patternCJK
-- >< patternCJK
-- mw.ustring.char()
-- mw.ustring.char()
-- mw.ustring.match()
-- mw.ustring.match()
local r = false
local r = false
patternCJK = patternCJK or mw.ustring.char(91,
if not Local.patternCJK then
13312, 45, 40959,
Local.patternCJK = mw.ustring.char( 91,
131072, 45, 178207,
13312, 45, 40959,
93 )
131072, 45, 178207,
if mw.ustring.match( s, patternCJK ) then
93 )
r = true
end
end
if mw.ustring.match( analyse, Local.patternCJK ) then
return r
r = true
end
return r
end -- containsCJK()
end -- containsCJK()


Line 216: Line 120:


local function facility( accept, attempt )
local function facility( accept, attempt )
-- Check string as possible file name or other source page
-- Check string as possible file name or other source page
-- Precondition:
-- Precondition:
-- accept -- string; requirement
-- accept -- string; requirement
-- file
-- file
-- file+
-- file+
-- file:
-- file:
-- file:+
-- file:+
-- image
-- image
-- image+
-- image+
-- image:
-- image:
-- image:+
-- image:+
-- attempt -- string; to be tested
-- attempt -- string; to be tested
-- Postcondition:
-- Postcondition:
-- Return error keyword, or false
-- Return error keyword, or false
-- Uses:
-- Uses:
-- Module:FileMedia
-- Module:FileMedia
-- FileMedia.isType()
-- Foreign()
local r
-- FileMedia.isFile()
if attempt and attempt ~= "" then
-- FileMedia.isType()
local lucky, FileMedia = pcall( require, "Module:FileMedia" )
local r
if attempt and attempt ~= "" then
if type( FileMedia ) == "table" then
local FileMedia = Foreign( "FileMedia" )
FileMedia = FileMedia.FileMedia()
local s, live = accept:match( "^([a-z]+)(:?)%+?$" )
if FileMedia and type( FileMedia.isFile ) == "function"
if live then
and type( FileMedia.isType ) == "function" then
if FileMedia.isType( attempt, s ) then
local s, live = accept:match( "^([a-z]+)(:?)%+?$" )
if FileMedia.isFile( attempt ) then
if live then
r = false
if FileMedia.isType( attempt, s ) then
else
if FileMedia.isFile( attempt ) then
r = "notFound"
r = false
end
else
else
r = "notFound"
r = "invalid"
end
end
else
elseif FileMedia.isType( attempt, s ) then
r = "invalid"
r = false
end
else
elseif FileMedia.isType( attempt, s ) then
r = "invalid"
r = false
end
else
else
r = "invalid"
r = "missing"
end
end
else
elseif accept:match( "%+$" ) then
r = "missing"
r = "empty"
end
else
elseif accept:match( "%+$" ) then
r = "empty"
r = false
end
else
return r
r = false
end
return r
end -- facility()
end -- facility()


Line 270: Line 172:


local function factory( say )
local function factory( say )
-- Retrieve localized message string in content language
-- Retrieve localized message string in content language
-- Precondition:
-- Precondition:
-- say -- string; message ID
-- say -- string; message ID
-- Postcondition:
-- Postcondition:
-- Return some message string
-- Return some message string
-- Uses:
-- Uses:
-- > MessagePrefix
-- > Local.messagePrefix
-- > Local.L10nDef
-- > L10nDef
-- mw.message.new()
-- mw.language.getContentLanguage()
-- mw.language.getContentLanguage()
-- mw.message.new()
local c = mw.language.getContentLanguage():getCode()
-- Module:Multilingual
local m = mw.message.new( MessagePrefix .. say )
-- Foreign()
local r = false
-- TemplatePar.framing()
if m:isBlank() then
-- Multilingual.tabData()
local l10n = L10nDef[ c ] or L10nDef[ "en" ]
local m = mw.message.new( Local.messagePrefix .. say )
r = l10n[ say ]
local r = false
else
if m:isBlank() then
m:inLanguage( c )
local c = mw.language.getContentLanguage():getCode()
r = m:plain()
local l10n = Local.L10nDef[ c ]
end
if l10n then
r = l10n[ say ]
r = r or string.format( "(((%s)))", say )
return r
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()
end -- factory()






local function faculty( accept, attempt )
local function failsafe( story, scan )
-- Test for match (possibly user-defined with syntax error)
-- Check string as possible boolean
-- Precondition:
-- Precondition:
-- accept -- string; requirement
-- story -- string; parameter value
-- scan -- string; pattern
-- boolean
-- Postcondition:
-- boolean+
-- Return nil, if not matching, else non-nil
-- attempt -- string; to be tested
-- Uses:
-- Postcondition:
-- mw.ustring.match()
-- Return error keyword, or false
return mw.ustring.match( story, scan )
-- Uses:
end -- failsafe()
-- 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 )
local function failure( spec, suspect, options )
-- Submit localized error message
-- Submit localized error message
-- Precondition:
-- Precondition:
-- spec -- string; message ID
-- spec -- string; message ID
-- suspect -- string or nil; additional information
-- suspect -- string or nil; additional information
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- options.template
-- options.template
-- Postcondition:
-- Postcondition:
-- Return string
-- Return string
-- Uses:
-- Uses:
-- factory()
-- factory()
local r = factory( spec )
local r = factory( spec )
if type( options ) == "table" then
if type( options ) == "table" then
if type( options.template ) == "string" then
if type( options.template ) == "string" then
if #options.template > 0 then
if #options.template > 0 then
r = string.format( "%s (%s)", r, options.template )
r = string.format( "%s (%s)", r, options.template )
end
end
end
end
end
end
if suspect then
if suspect then
r = string.format( "%s: %s", r, suspect )
r = string.format( "%s: %s", r, suspect )
end
end
return r
return r
end -- failure()
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()






local function fault( store, key )
local function fault( store, key )
-- Add key to collection string and insert separator
-- Add key to collection string and insert separator
-- Precondition:
-- Precondition:
-- store -- string or nil or false; collection string
-- store -- string or nil or false; collection string
-- key -- string or number; to be appended
-- key -- string or number; to be appended
-- Postcondition:
-- Postcondition:
-- Return string; extended
-- Return string; extended
local r
local r
local s
local s
if type( key ) == "number" then
if type( key ) == "number" then
s = tostring( key )
s = tostring( key )
else
else
s = key
s = key
end
end
if store then
if store then
r = string.format( "%s; %s", store, s )
r = string.format( "%s; %s", store, s )
else
else
r = s
r = s
end
end
return r
return r
end -- fault()
end -- fault()


Line 575: Line 264:


local function feasible( analyze, options, abbr )
local function feasible( analyze, options, abbr )
-- Check content of a value
-- Check content of a value
-- Precondition:
-- Precondition:
-- analyze -- string to be analyzed
-- analyze -- string to be analyzed
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- options.pattern
-- options.pattern
-- options.key
-- options.key
-- options.say
-- options.say
-- abbr -- true: abbreviated error message
-- abbr -- true: abbreviated error message
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid or no answer permitted
-- false if valid or no answer permitted
-- Uses:
-- Uses:
-- > Local.patterns
-- > Patterns
-- failure()
-- failure()
-- mw.text.trim()
-- mw.text.trim()
-- faculty()
-- facility()
-- fast()
-- failsafe()
-- facility()
-- containsCJK()
local r = false
-- familiar()
local s = false
-- far()
local show = nil
-- fair()
local scan = false
-- containsCJK()
if type( options.pattern ) == "string" then
local r = false
if options.key then
local s = false
r = failure( "dupRule", false, options )
local show = nil
else
local scan = false
scan = options.pattern
local stuff = mw.text.trim( analyze )
end
if type( options.pattern ) == "string" then
else
if options.key then
if type( options.key ) == "string" then
r = failure( "dupRule", false, options )
s = mw.text.trim( options.key )
else
else
scan = options.pattern
s = "+"
end
end
else
if type( options.key ) == "string" then
if s ~= "*" then
scan = Patterns[ s ]
s = mw.text.trim( options.key )
end
else
if type( scan ) == "string" then
s = "+"
if s == "n" or s == "0,0" or s == "0.0" then
end
if not analyze:match( "[0-9]" ) and
if s ~= "*" then
not analyze:match( "^%s*$" ) then
scan = Local.patterns[ s ]
scan = false
end
if options.say then
if type( scan ) == "string" then
show = string.format( "'%s'", options.say )
if s == "n" or s == "0,0" or s == "0.0" then
end
if not stuff:match( "[0-9]" ) and
if abbr then
not stuff:match( "^%s*$" ) then
r = show
scan = false
else
if options.say then
show = string.format( "&quot;%s&quot;", options.say )
r = failure( "invalid", show, options )
end
end
end
if abbr then
end
r = show
elseif s ~= "*" then
else
local op, n, plus = s:match( "([<!=>]=?)([-0-9][%S]*)(+?)" )
r = failure( "invalid", show, options )
if op then
end
n = tonumber( n )
end
if n then
end
local i = tonumber( analyze )
elseif s ~= "*" then
if i then
local op, n, plus = s:match( "([<!=>]=?)([-0-9][%S]*)(+?)" )
if op then
if op == "<" then
i = ( i < n )
n = tonumber( n )
elseif op == "<=" then
if n then
i = ( i <= n )
local i = tonumber( stuff )
elseif op == ">" then
if i then
i = ( i > n )
if op == "<" then
elseif op == ">=" then
i = ( i < n )
i = ( i >= n )
elseif op == "<=" then
elseif op == "==" then
i = ( i <= n )
i = ( i == n )
elseif op == ">" then
elseif op == "!=" then
i = ( i > n )
i = ( i ~= n )
elseif op == ">=" then
else
i = ( i >= n )
n = false
elseif op == "==" then
end
i = ( i == n )
end
elseif op == "!=" then
if not i then
i = ( i ~= n )
r = "invalid"
else
end
n = false
elseif plus then
end
r = "undefined"
end
end
if not i then
elseif s:match( "^image%+?:?$" ) or
r = "invalid"
s:match( "^file%+?:?$" ) then
end
r = facility( s, analyze )
elseif plus then
n = true
r = "undefined"
elseif s:match( "langW?%+?" ) then
end
n = "lang"
elseif s:match( "^boolean%+?$" ) then
-- lang lang+
r = faculty( s, stuff )
-- langW langW+
n = true
end
elseif s:match( "^datetime/?y?m?d?%+?$" ) then
if not n and not r then
r = fast( s, stuff )
r = "unknownRule"
n = true
end
elseif s:match( "^image%+?:?$" ) or
if r then
s:match( "^file%+?:?$" ) then
if options.say then
r = facility( s, stuff )
show = string.format( "'%s' %s", options.say, s )
n = true
else
elseif s:match( "langs?W?%+?" ) then
show = s
r = familiar( s, stuff )
end
n = true
if abbr then
elseif s:match( "url%+?" ) then
r = show
r = far( s, stuff )
else
n = true
r = failure( r, show, options )
end
end
-- datetime+
end
-- iso8631+
end
-- line+
end
if not n and not r then
if scan then
r = "unknownRule"
local legal, got = pcall( failsafe, analyze, scan )
end
if r then
if legal then
if not got then
if options.say then
if s == "aa" then
show = string.format( "&quot;%s&quot; %s", options.say, s )
got = containsCJK( analyze )
else
end
show = s
if not got then
end
if abbr then
if options.say then
show = string.format( "'%s'", options.say )
r = show
end
else
if abbr then
r = failure( r, show, options )
r = show
end
else
end
r = failure( "invalid", show, options )
end
end
end
end
if scan then
end
local legal, got = pcall( fair, stuff, scan )
else
if legal then
r = failure( "badPattern",
if not got then
string.format( "%s *** %s", scan, got ),
if s == "aa" then
options )
got = containsCJK( stuff )
end
end
end
if not got then
return r
if options.say then
show = string.format( "&quot;%s&quot;", options.say )
end
if abbr then
r = show
else
r = failure( "invalid", show, options )
end
end
end
else
r = failure( "badPattern",
string.format( "%s *** %s", scan, got ),
options )
end
end
return r
end -- feasible()
end -- feasible()


Line 728: Line 401:


local function fed( haystack, needle )
local function fed( haystack, needle )
-- Find needle in haystack map
-- Find needle in haystack map
-- Precondition:
-- Precondition:
-- haystack -- table; map of key values
-- haystack -- table; map of key values
-- needle -- any; identifier
-- needle -- any; identifier
-- Postcondition:
-- Postcondition:
-- Return true iff found
-- Return true iff found
local k, v, r
local k, v
for k, v in pairs( haystack ) do
for k, v in pairs( haystack ) do
if k == needle then
if k == needle then
r = true
return true
end
end
end -- for k, v
end -- for k, v
return r or false
return false
end -- fed()
end -- fed()


Line 746: Line 419:


local function fetch( light, options )
local function fetch( light, options )
-- Return regular table with all parameters
-- Return regular table with all parameters
-- Precondition:
-- Precondition:
-- light -- true: template transclusion; false: #invoke
-- light -- true: template transclusion; false: #invoke
-- options -- table; optional details
-- options -- table; optional details
-- options.low
-- options.low
-- Postcondition:
-- Postcondition:
-- Return table; whitespace-only values as false
-- Return table; whitespace-only values as false
-- Uses:
-- Uses:
-- TemplatePar.downcase()
-- TemplatePar.downcase()
-- mw.getCurrentFrame()
-- TemplatePar.framing()
-- frame:getParent()
-- frame:getParent()
local g, k, v
local g, k, v
local r = { }
local r = { }
if options.low then
if options.low then
g = TemplatePar.downcase( options )
g = TemplatePar.downcase( options )
else
else
g = mw.getCurrentFrame()
g = TemplatePar.framing()
if light then
if light then
g = g:getParent()
g = g:getParent()
end
end
g = g.args
g = g.args
end
end
if type( g ) == "table" then
if type( g ) == "table" then
r = { }
r = { }
for k, v in pairs( g ) do
for k, v in pairs( g ) do
if type( v ) == "string" then
if type( v ) == "string" then
if v:match( "^%s*$" ) then
if v:match( "^%s*$" ) then
v = false
v = false
end
end
else
else
v = false
v = false
end
end
if type( k ) == "number" then
if type( k ) == "number" then
k = tostring( k )
k = tostring( k )
end
end
r[ k ] = v
r[ k ] = v
end -- for k, v
end -- for k, v
else
else
r = g
r = g
end
end
return r
return r
end -- fetch()
end -- fetch()


Line 792: Line 465:


local function figure( append, options )
local function figure( append, options )
-- Extend options by rule from #invoke strings
-- Extend options by rule from #invoke strings
-- Precondition:
-- Precondition:
-- append -- string or nil; requested rule
-- append -- string or nil; requested rule
-- options -- table; details
-- options -- table; details
-- ++ .key
-- ++ .key
-- ++ .pattern
-- ++ .pattern
-- Postcondition:
-- Postcondition:
-- Return sequence table
-- Return sequence table
local r = options
local r = options
if type( append ) == "string" then
if type( append ) == "string" then
local story = mw.text.trim( append )
local story = mw.text.trim( append )
local sub = story:match( "^/(.*%S)/$" )
local sub = story:match( "^/(.*%S)/$" )
if type( sub ) == "string" then
if type( sub ) == "string" then
sub = sub:gsub( "%%!", "|" )
sub = sub:gsub( "%%!", "|" )
:gsub( "%%%(%(", "{{" )
sub = sub:gsub( "%%%(%(", "{{" )
:gsub( "%%%)%)", "}}" )
sub = sub:gsub( "%%%)%)", "}}" )
options.pattern = sub
:gsub( "\\n", string.char( 10 ) )
options.pattern = sub
options.key = nil
else
options.key = nil
options.key = story
else
options.key = story
options.pattern = nil
end
options.pattern = nil
end
end
return r
end
return r
end -- figure()
end -- figure()


Line 822: Line 494:


local function fill( specified )
local function fill( specified )
-- Split requirement string separated by '='
-- Split requirement string separated by '='
-- Precondition:
-- Precondition:
-- specified -- string or nil; requested parameter set
-- specified -- string or nil; requested parameter set
-- Postcondition:
-- Postcondition:
-- Return sequence table
-- Return sequence table
-- Uses:
-- Uses:
-- mw.text.split()
-- mw.text.split()
local r
local r
if specified then
if specified then
local i, s
local i, s
r = mw.text.split( specified, "%s*=%s*" )
r = mw.text.split( specified, "%s*=%s*" )
for i = #r, 1, -1 do
for i = #r, 1, -1 do
s = r[ i ]
s = r[ i ]
if #s == 0 then
if #s == 0 then
table.remove( r, i )
table.remove( r, i )
end
end
end -- for i, -1
end -- for i, -1
else
else
r = { }
r = { }
end
end
return r
return r
end -- fill()
end -- fill()






local function finalize( submit, options )
local function finalize( submit, options, frame )
-- Finalize message
-- Finalize message
-- Precondition:
-- Precondition:
-- submit -- string or false or nil; non-empty error message
-- submit -- string or false or nil; non-empty error message
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- options.format
-- options.format
-- options.preview
-- options.preview
-- options.cat
-- options.cat
-- options.template
-- options.template
-- frame -- object, or false
-- Postcondition:
-- Postcondition:
-- Return string or false
-- Return string or false
-- Uses:
-- Uses:
-- TemplatePar.framing()
-- factory()
-- factory()
local r = false
local r = false
if submit then
if submit then
local lazy = false
local opt, s
local learn = false
local lazy = false
local show = false
local show = false
if type( options ) == "table" then
local opt, s
if type( options ) == "table" then
opt = options
show = opt.format
opt = options
show = opt.format
lazy = ( show == "" or show == "0" or show == "-" )
s = opt.preview
lazy = ( show == "" or show == "0" or show == "-" )
if type( s ) == "string" and
s = opt.preview
if type( s ) == "string" and
s ~= "" and s ~= "0" and s ~= "-" then
if lazy then
s ~= "" and s ~= "0" and s ~= "-" then
show = ""
local sniffer = "{{REVISIONID}}"
lazy = false
if lazy then
end
show = ""
frame = frame or mw.getCurrentFrame()
lazy = false
if frame:preprocess( "{{REVISIONID}}" ) == "" then
end
if s == "1" then
if TemplatePar.framing():preprocess( sniffer ) == "" then
show = "*"
if s == "1" then
else
show = "*"
show = s
else
end
show = s
end
end
end
learn = true
else
end
opt = { }
end
end
else
if lazy then
opt = { }
if not opt.cat then
end
r = string.format( "%s %s",
if lazy then
submit, factory( "noMSGnoCAT" ) )
if not opt.cat then
end
r = string.format( "%s %s",
else
submit, factory( "noMSGnoCAT" ) )
r = submit
end
end
else
if r and not lazy then
r = submit
local i
end
if r and not lazy then
if not show or show == "*" then
show = "<span class=\"error\">@@@</span>"
local i
end
if not show or show == "*" then
i = show:find( "@@@", 1, true )
local e = mw.html.create( "span" )
if i then
:attr( "class", "error" )
-- No gsub() since r might contain "%3" (e.g. URL)
:wikitext( "@@@" )
r = string.format( "%s%s%s",
if learn then
show:sub( 1, i - 1 ),
local max = 1000000000
r,
local id = math.floor( os.clock() * max )
show:sub( i + 3 ) )
local sign = string.format( "error_%d", id )
else
local btn = mw.html.create( "span" )
r = show
local top = mw.html.create( "div" )
end
e:attr( "id", sign )
end
btn:css( { ["background"] = "#FFFF00",
s = opt.cat
["border"] = "#FF0000 3px solid",
if type( s ) == "string" then
["font-weight"] = "bold",
if opt.errNS then
["padding"] = "2px",
local ns = mw.title.getCurrentTitle().namespace
["text-decoration"] = "none" } )
local st = type( opt.errNS )
:wikitext( "&gt;&gt;&gt;" )
if st == "string" then
sign = string.format( "[[#%s|%s]]",
local space = string.format( ".*%%s%d%%s.*", ns )
sign, tostring( btn ) )
local spaces = string.format( " %s ", opt.errNS )
top:wikitext( sign, "&#160;", submit )
if spaces:match( space ) then
mw.addWarning( tostring( top ) )
opt.errNS = false
end
end
show = tostring( e )
elseif st == "table" then
end
for i = 1, #opt.errNS do
i = show:find( "@@@", 1, true )
if i then
if opt.errNS[ i ] == ns then
opt.errNS = false
-- No gsub() since r might contain "%3" (e.g. URL)
break -- for i
r = string.format( "%s%s%s",
end
show:sub( 1, i - 1 ),
end -- for i
r,
end
show:sub( i + 3 ) )
end
else
if opt.errNS then
r = show
r = ""
end
else
end
r = r or ""
if learn and r then
if s:find( "@@@" ) then
-- r = fatal( r )
if type( opt.template ) == "string" then
end
s = opt.cat
s = s:gsub( "@@@", opt.template )
end
if type( s ) == "string" then
end
local link
local i
if opt.errNS then
local cats = mw.text.split( s, "%s*#%s*" )
local ns = mw.title.getCurrentTitle().namespace
for i = 1, #cats do
local st = type( opt.errNS )
s = mw.text.trim( cats[ i ] )
if st == "string" then
if #s > 0 then
local space = string.format( ".*%%s%d%%s.*", ns )
local spaces = string.format( " %s ", opt.errNS )
r = string.format( "%s[[Category:%s]]", r, s )
end
if spaces:match( space ) then
end -- for i
link = true
end
end
end
elseif st == "table" then
end
for i = 1, #opt.errNS do
return r
if opt.errNS[ i ] == ns then
link = true
break -- for i
end
end -- for i
end
else
link = true
end
if link then
local cats, i
if not r 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
end
end
return r
end -- finalize()
end -- finalize()


Line 988: Line 631:


local function finder( haystack, needle )
local function finder( haystack, needle )
-- Find needle in haystack sequence
-- Find needle in haystack sequence
-- Precondition:
-- Precondition:
-- haystack -- table; sequence of key names, downcased if low
-- haystack -- table; sequence of key names, downcased if low
-- needle -- any; key name
-- needle -- any; key name
-- Postcondition:
-- Postcondition:
-- Return true iff found
-- Return true iff found
local i
local i
for i = 1, #haystack do
for i = 1, #haystack do
if haystack[ i ] == needle then
if haystack[ i ] == needle then
return true
return true
end
end
end -- for i
end -- for i
return false
return false
end -- finder()
end -- finder()


Line 1,006: Line 649:


local function fix( valid, duty, got, options )
local function fix( valid, duty, got, options )
-- Perform parameter analysis
-- Perform parameter analysis
-- Precondition:
-- Precondition:
-- valid -- table; unique sequence of known parameters
-- valid -- table; unique sequence of known parameters
-- duty -- table; sequence of mandatory parameters
-- duty -- table; sequence of mandatory parameters
-- got -- table; sequence of current parameters
-- got -- table; sequence of current parameters
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- Postcondition:
-- Postcondition:
-- Return string as configured; empty if valid
-- Return string as configured; empty if valid
-- Uses:
-- Uses:
-- finder()
-- finder()
-- fault()
-- fault()
-- failure()
-- failure()
-- fed()
-- fed()
local r = false
local k, v
local r = false
local lack
for k, v in pairs( got ) do
for k, v in pairs( got ) do
if k == "" then
if not finder( valid, k ) then
r = fault( r, k )
lack = true
end
break -- for k, v
end -- for k, v
elseif not finder( valid, k ) then
if r then
r = fault( r, k )
r = failure( "unknown",
end
string.format( "'%s'", r ),
end -- for k, v
options )
if lack then
else -- all names valid
r = failure( "unavailable", false, options )
local i, s
elseif r then
for i = 1, #duty do
r = failure( "unknown",
s = duty[ i ]
string.format( "&quot;%s&quot;", r ),
if not fed( got, s ) then
options )
r = fault( r, s )
else -- all names valid
end
local i, s
for i = 1, #duty do
end -- for i
if r then
s = duty[ i ]
r = failure( "undefined", r, options )
if not fed( got, s ) then
else -- all mandatory present
r = fault( r, s )
for i = 1, #duty do
end
s = duty[ i ]
end -- for i
if r then
if not got[ s ] then
r = failure( "undefined", r, options )
r = fault( r, s )
end
else -- all mandatory present
end -- for i
for i = 1, #duty do
if r then
s = duty[ i ]
r = failure( "empty", r, options )
if not got[ s ] then
end
r = fault( r, s )
end
end
end
end -- for i
return r
if r then
r = failure( "empty", r, options )
end
end
end
return r
end -- fix()
end -- fix()


Line 1,063: Line 701:


local function flat( collection, options )
local function flat( collection, options )
-- Return all table elements with downcased string
-- Return all table elements with downcased string
-- Precondition:
-- Precondition:
-- collection -- table; k=v pairs
-- collection -- table; k=v pairs
-- options -- table or nil; optional messaging details
-- options -- table or nil; optional messaging details
-- Postcondition:
-- Postcondition:
-- Return table, may be empty; or string with error message.
-- Return table, may be empty; or string with error message.
-- Uses:
-- Uses:
-- mw.ustring.lower()
-- mw.ustring.lower()
-- fault()
-- fault()
-- failure()
-- failure()
local k, v
local k, v
local r = { }
local r = { }
local e = false
local e = false
for k, v in pairs( collection ) do
for k, v in pairs( collection ) do
if type ( k ) == "string" then
if type ( k ) == "string" then
k = mw.ustring.lower( k )
k = mw.ustring.lower( k )
if r[ k ] then
if r[ k ] then
e = fault( e, k )
e = fault( e, k )
end
end
end
end
r[ k ] = v
r[ k ] = v
end -- for k, v
end -- for k, v
if e then
if e then
r = failure( "multiSpell", e, options )
r = failure( "multiSpell", e, options )
end
end
return r
return r
end -- flat()
end -- flat()


Line 1,094: Line 732:


local function fold( options )
local function fold( options )
-- Merge two tables, create new sequence if both not empty
-- Merge two tables, create new sequence if both not empty
-- Precondition:
-- Precondition:
-- options -- table; details
-- options -- table; details
-- options.mandatory sequence to keep unchanged
-- options.mandatory sequence to keep unchanged
-- options.optional sequence to be appended
-- options.optional sequence to be appended
-- options.low downcased expected
-- options.low downcased expected
-- Postcondition:
-- Postcondition:
-- Return merged table, or message string if error
-- Return merged table, or message string if error
-- Uses:
-- Uses:
-- finder()
-- finder()
-- fault()
-- fault()
-- failure()
-- failure()
-- flat()
-- flat()
local i, e, r, s
local i, e, r, s
local base = options.mandatory
local base = options.mandatory
local extend = options.optional
local extend = options.optional
if #base == 0 then
if #base == 0 then
if #extend == 0 then
if #extend == 0 then
r = { }
r = { }
else
else
r = extend
r = extend
end
end
else
else
if #extend == 0 then
if #extend == 0 then
r = base
r = base
else
else
e = false
e = false
for i = 1, #extend do
for i = 1, #extend do
s = extend[ i ]
s = extend[ i ]
if finder( base, s ) then
if finder( base, s ) then
e = fault( e, s )
e = fault( e, s )
end
end
end -- for i
end -- for i
if e then
if e then
r = failure( "dupOpt", e, options )
r = failure( "dupOpt", e, options )
else
else
r = { }
r = { }
for i = 1, #base do
for i = 1, #base do
table.insert( r, base[ i ] )
table.insert( r, base[ i ] )
end -- for i
end -- for i
for i = 1, #extend do
for i = 1, #extend do
table.insert( r, extend[ i ] )
table.insert( r, extend[ i ] )
end -- for i
end -- for i
end
end
end
end
end
end
if options.low and type( r ) == "table" then
if options.low and type( r ) == "table" then
r = flat( r, options )
r = flat( r, options )
end
end
return r
return r
end -- fold()
end -- fold()


Line 1,149: Line 787:


local function form( light, options, frame )
local function form( light, options, frame )
-- Run parameter analysis on current environment
-- Run parameter analysis on current environment
-- Precondition:
-- Precondition:
-- light -- true: template transclusion; false: #invoke
-- light -- true: template transclusion; false: #invoke
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- options.mandatory
-- options.mandatory
-- options.optional
-- options.optional
-- frame -- object; #invoke environment, or false
-- frame -- object, or false
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid
-- false if valid
-- Uses:
-- Uses:
-- fold()
-- TemplatePar.framing()
-- fold()
-- fetch()
-- fetch()
-- fix()
-- fix()
-- finalize()
local duty, r
-- finalize()
if type( options ) == "table" then
local duty, r
if frame then
if type( options.mandatory ) ~= "table" then
options.mandatory = { }
TemplatePar.framing( frame )
end
end
if type( options ) == "table" then
duty = options.mandatory
if type( options.mandatory ) ~= "table" then
if type( options.optional ) ~= "table" then
options.mandatory = { }
options.optional = { }
end
end
r = fold( options )
duty = options.mandatory
else
if type( options.optional ) ~= "table" then
options.optional = { }
options = { }
duty = { }
end
r = { }
r = fold( options )
end
else
if type( r ) == "table" then
options = { }
local got = fetch( light, options )
duty = { }
if type( got ) == "table" then
r = { }
r = fix( r, duty, got, options )
end
else
if type( r ) == "table" then
r = got
local got = fetch( light, options )
end
if type( got ) == "table" then
end
r = fix( r, duty, got, options )
return finalize( r, options, frame )
else
r = got
end
end
return finalize( r, options )
end -- form()
end -- form()


Line 1,197: Line 831:


local function format( analyze, options )
local function format( analyze, options )
-- Check validity of a value
-- Check validity of a value
-- Precondition:
-- Precondition:
-- analyze -- string to be analyzed
-- analyze -- string to be analyzed
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- options.say
-- options.say
-- options.min
-- options.min
-- options.max
-- options.max
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid or no answer permitted
-- false if valid or no answer permitted
-- Uses:
-- Uses:
-- feasible()
-- feasible()
-- failure()
-- failure()
local r = feasible( analyze, options, false )
local r = feasible( analyze, options, false )
local show
local show
if options.min and not r then
if options.min and not r then
if type( options.min ) == "number" then
if type( options.min ) == "number" then
if type( options.max ) == "number" then
if type( options.max ) == "number" then
if options.max < options.min then
if options.max < options.min then
r = failure( "minmax",
r = failure( "minmax",
string.format( "%d > %d",
string.format( "%d > %d",
options.min,
options.min,
options.max ),
options.max ),
options )
options )
end
end
end
end
if #analyze < options.min and not r then
if #analyze < options.min and not r then
show = " <" .. options.min
show = " <" .. options.min
if options.say then
if options.say then
show = string.format( "%s &quot;%s&quot;", show, options.say )
show = string.format( "%s '%s'", show, options.say )
end
end
r = failure( "tooShort", show, options )
r = failure( "tooShort", show, options )
end
end
else
else
r = failure( "invalidPar", "min", options )
r = failure( "invalidPar", "min", options )
end
end
end
end
if options.max and not r then
if options.max and not r then
if type( options.max ) == "number" then
if type( options.max ) == "number" then
if #analyze > options.max then
if #analyze > options.max then
show = " >" .. options.max
show = " >" .. options.max
if options.say then
if options.say then
show = string.format( "%s &quot;%s&quot;", show, options.say )
show = string.format( "%s '%s'", show, options.say )
end
end
r = failure( "tooLong", show, options )
r = failure( "tooLong", show, options )
end
end
else
else
r = failure( "invalidPar", "max", options )
r = failure( "invalidPar", "max", options )
end
end
end
end
return r
return r
end -- format()
end -- format()


Line 1,253: Line 887:


local function formatted( assignment, access, options )
local function formatted( assignment, access, options )
-- Check validity of one particular parameter in a collection
-- Check validity of one particular parameter in a collection
-- Precondition:
-- Precondition:
-- assignment -- collection
-- assignment -- collection
-- access -- id of parameter in collection
-- access -- id of parameter in collection
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid or no answer permitted
-- false if valid or no answer permitted
-- Uses:
-- Uses:
-- mw.text.trim()
-- mw.text.trim()
-- format()
-- format()
-- failure()
-- failure()
local r = false
local r = false
if type( assignment ) == "table" then
if type( assignment ) == "table" then
local story = assignment.args[ access ] or ""
local story = assignment.args[ access ] or ""
if type( access ) == "number" then
if type( access ) == "number" then
story = mw.text.trim( story )
story = mw.text.trim( story )
end
end
if type( options ) ~= "table" then
if type( options ) ~= "table" then
options = { }
options = { }
end
end
options.say = access
options.say = access
r = format( story, options )
r = format( story, options )
end
end
return r
return r
end -- formatted()
end -- formatted()


Line 1,283: Line 917:


local function furnish( frame, action )
local function furnish( frame, action )
-- Prepare #invoke evaluation of .assert() or .valid()
-- Prepare #invoke evaluation of .assert() or .valid()
-- Precondition:
-- Precondition:
-- frame -- object; #invoke environment
-- frame -- object; #invoke environment
-- action -- "assert" or "valid"
-- action -- "assert" or "valid"
-- Postcondition:
-- Postcondition:
-- Return string with error message or ""
-- Return string with error message or ""
-- Uses:
-- Uses:
-- form()
-- form()
-- failure()
-- failure()
-- finalize()
-- finalize()
-- TemplatePar.valid()
-- TemplatePar.valid()
-- TemplatePar.assert()
-- TemplatePar.assert()
local options = { mandatory = { "1" },
local options = { mandatory = { "1" },
optional = { "2",
optional = { "2",
"cat",
"cat",
"errNS",
"errNS",
"low",
"low",
"max",
"max",
"min",
"min",
"format",
"format",
"preview",
"preview",
"template" },
"template" },
template = string.format( "&#35;invoke:%s|%s|",
template = string.format( "&#35;invoke:%s|%s|",
"TemplatePar",
"TemplatePar",
action )
action )
}
}
local r = form( false, options, frame )
local r = form( false, options, frame )
if not r then
if not r then
local s
local s
options = { cat = frame.args.cat,
options = { cat = frame.args.cat,
errNS = frame.args.errNS,
errNS = frame.args.errNS,
low = frame.args.low,
low = frame.args.low,
format = frame.args.format,
format = frame.args.format,
preview = frame.args.preview,
preview = frame.args.preview,
template = frame.args.template
template = frame.args.template
}
}
options = figure( frame.args[ 2 ], options )
options = figure( frame.args[ 2 ], options )
if type( frame.args.min ) == "string" then
if type( frame.args.min ) == "string" then
s = frame.args.min:match( "^%s*([0-9]+)%s*$" )
s = frame.args.min:match( "^%s*([0-9]+)%s*$" )
if s then
if s then
options.min = tonumber( s )
options.min = tonumber( s )
else
else
r = failure( "invalidPar",
r = failure( "invalidPar",
"min=" .. frame.args.min,
"min=" .. frame.args.min,
options )
options )
end
end
end
end
if type( frame.args.max ) == "string" then
if type( frame.args.max ) == "string" then
s = frame.args.max:match( "^%s*([1-9][0-9]*)%s*$" )
s = frame.args.max:match( "^%s*([1-9][0-9]*)%s*$" )
if s then
if s then
options.max = tonumber( s )
options.max = tonumber( s )
else
else
r = failure( "invalidPar",
r = failure( "invalidPar",
"max=" .. frame.args.max,
"max=" .. frame.args.max,
options )
options )
end
end
end
end
if r then
if r then
r = finalize( r, options )
r = finalize( r, options, frame )
else
else
s = frame.args[ 1 ] or ""
s = frame.args[ 1 ] or ""
r = tonumber( s )
r = tonumber( s )
if ( r ) then
if ( r ) then
s = r
s = r
end
end
if action == "valid" then
if action == "valid" then
r = TemplatePar.valid( s, options )
r = TemplatePar.valid( s, options, frame )
elseif action == "assert" then
elseif action == "assert" then
r = TemplatePar.assert( s, "", options )
r = TemplatePar.assert( s, "", options )
end
end
end
end
end
end
return r or ""
return r or ""
end -- furnish()
end -- furnish()


Line 1,361: Line 995:


TemplatePar.assert = function ( analyze, append, options )
TemplatePar.assert = function ( analyze, append, options )
-- Perform parameter analysis on a single string
-- Perform parameter analysis on a single string
-- Precondition:
-- Precondition:
-- analyze -- string to be analyzed
-- analyze -- string to be analyzed
-- append -- string: append error message, prepending <br />
-- append -- string: append error message, prepending <br />
-- false or nil: throw error with message
-- false or nil: throw error with message
-- options -- table; optional details
-- options -- table; optional details
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid
-- false if valid
-- Uses:
-- Uses:
-- format()
-- format()
local r = format( analyze, options )
local r = format( analyze, options )
if ( r ) then
if ( r ) then
if ( type( append ) == "string" ) then
if ( type( append ) == "string" ) then
if ( append ~= "" ) then
if ( append ~= "" ) then
r = string.format( "%s<br /> %s", append, r )
r = string.format( "%s<br />%s", append, r )
end
end
else
else
error( r, 0 )
error( r, 0 )
end
end
end
end
return r
return r
end -- TemplatePar.assert()
end -- TemplatePar.assert()


Line 1,388: Line 1,022:


TemplatePar.check = function ( options )
TemplatePar.check = function ( options )
-- Run parameter analysis on current template environment
-- Run parameter analysis on current template environment
-- Precondition:
-- Precondition:
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- options.mandatory
-- options.mandatory
-- options.optional
-- options.optional
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid
-- false if valid
-- Uses:
-- Uses:
-- form()
-- form()
return form( true, options, false )
return form( true, options, false )
end -- TemplatePar.check()
end -- TemplatePar.check()


Line 1,404: Line 1,038:


TemplatePar.count = function ()
TemplatePar.count = function ()
-- Return number of template parameters
-- Return number of template parameters
-- Postcondition:
-- Postcondition:
-- Return number, starting at 0
-- Return number, starting at 0
-- Uses:
-- Uses:
-- mw.getCurrentFrame()
-- mw.getCurrentFrame()
-- frame:getParent()
-- frame:getParent()
local k, v
local k, v
local r = 0
local r = 0
local t = mw.getCurrentFrame():getParent()
local t = mw.getCurrentFrame():getParent()
local o = t.args
local o = t.args
for k, v in pairs( o ) do
for k, v in pairs( o ) do
r = r + 1
r = r + 1
end -- for k, v
end -- for k, v
return r
return r
end -- TemplatePar.count()
end -- TemplatePar.count()


Line 1,423: Line 1,057:


TemplatePar.countNotEmpty = function ()
TemplatePar.countNotEmpty = function ()
-- Return number of template parameters with more than whitespace
-- Return number of template parameters with more than whitespace
-- Postcondition:
-- Postcondition:
-- Return number, starting at 0
-- Return number, starting at 0
-- Uses:
-- Uses:
-- mw.getCurrentFrame()
-- mw.getCurrentFrame()
-- frame:getParent()
-- frame:getParent()
local k, v
local k, v
local r = 0
local r = 0
local t = mw.getCurrentFrame():getParent()
local t = mw.getCurrentFrame():getParent()
local o = t.args
local o = t.args
for k, v in pairs( o ) do
for k, v in pairs( o ) do
if not v:match( "^%s*$" ) then
if not v:match( "^%s*$" ) then
r = r + 1
r = r + 1
end
end
end -- for k, v
end -- for k, v
return r
return r
end -- TemplatePar.countNotEmpty()
end -- TemplatePar.countNotEmpty()


Line 1,444: Line 1,078:


TemplatePar.downcase = function ( options )
TemplatePar.downcase = function ( options )
-- Return all template parameters with downcased name
-- Return all template parameters with downcased name
-- Precondition:
-- Precondition:
-- options -- table or nil; optional messaging details
-- options -- table or nil; optional messaging details
-- Postcondition:
-- Postcondition:
-- Return table, may be empty; or string with error message.
-- Return table, may be empty; or string with error message.
-- Uses:
-- Uses:
-- mw.getCurrentFrame()
-- mw.getCurrentFrame()
-- frame:getParent()
-- frame:getParent()
-- flat()
-- flat()
local t = mw.getCurrentFrame():getParent()
local t = mw.getCurrentFrame():getParent()
return flat( t.args, options )
return flat( t.args, options )
end -- TemplatePar.downcase()
end -- TemplatePar.downcase()






TemplatePar.valid = function ( access, options )
TemplatePar.valid = function ( access, options, frame )
-- Check validity of one particular template parameter
-- Check validity of one particular template parameter
-- Precondition:
-- Precondition:
-- access -- id of parameter in template transclusion
-- access -- id of parameter in template transclusion
-- string or number
-- string or number
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- frame -- object; #invoke environment
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid or no answer permitted
-- false if valid or no answer permitted
-- Uses:
-- Uses:
-- mw.text.trim()
-- mw.text.trim()
-- TemplatePar.downcase()
-- TemplatePar.framing()
-- TemplatePar.downcase()
-- frame:getParent()
-- frame:getParent()
-- formatted()
-- formatted()
-- failure()
-- failure()
-- finalize()
-- finalize()
local r = type( access )
local r = type( access )
if r == "string" then
if r == "string" then
r = mw.text.trim( access )
r = mw.text.trim( access )
if #r == 0 then
if #r == 0 then
r = false
r = false
end
end
elseif r == "number" then
elseif r == "number" then
r = access
r = access
else
else
r = false
r = false
end
end
if r then
if r then
local params
local params
if type( options ) ~= "table" then
if type( options ) ~= "table" then
options = { }
options = { }
end
end
if options.low then
if options.low then
params = TemplatePar.downcase( options )
params = TemplatePar.downcase( options )
else
else
params = TemplatePar.framing():getParent()
params = frame:getParent()
end
end
r = formatted( params, access, options )
r = formatted( params, access, options )
else
else
r = failure( "noname", false, options )
r = failure( "noname", false, options )
end
end
return finalize( r, options )
return finalize( r, options, frame )
end -- TemplatePar.valid()
end -- TemplatePar.valid()


Line 1,507: Line 1,141:


TemplatePar.verify = function ( options )
TemplatePar.verify = function ( options )
-- Perform #invoke parameter analysis
-- Perform #invoke parameter analysis
-- Precondition:
-- Precondition:
-- options -- table or nil; optional details
-- options -- table or nil; optional details
-- Postcondition:
-- Postcondition:
-- Return string with error message as configured;
-- Return string with error message as configured;
-- false if valid
-- false if valid
-- Uses:
-- Uses:
-- form()
-- form()
return form( false, options, false )
return form( false, options, false )
end -- TemplatePar.verify()
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,609: Line 1,160:


function p.assert( frame )
function p.assert( frame )
-- Perform parameter analysis on some single string
-- Perform parameter analysis on some single string
-- Precondition:
-- Precondition:
-- frame -- object; #invoke environment
-- frame -- object; #invoke environment
-- Postcondition:
-- Postcondition:
-- Return string with error message or ""
-- Return string with error message or ""
-- Uses:
-- Uses:
-- furnish()
-- furnish()
return furnish( frame, "assert" )
return furnish( frame, "assert" )
end -- p.assert()
end -- .assert()






function p.check( frame )
function p.check( frame )
-- Check validity of template parameters
-- Check validity of template parameters
-- Precondition:
-- Precondition:
-- frame -- object; #invoke environment
-- frame -- object; #invoke environment
-- Postcondition:
-- Postcondition:
-- Return string with error message or ""
-- Return string with error message or ""
-- Uses:
-- Uses:
-- form()
-- form()
-- fill()
-- fill()
local options = { optional = { "all",
local options = { optional = { "all",
"opt",
"opt",
"cat",
"cat",
"errNS",
"errNS",
"low",
"low",
"format",
"format",
"preview",
"preview",
"template" },
"template" },
template = "&#35;invoke:TemplatePar|check|"
template = "&#35;invoke:TemplatePar|check|"
}
}
local r = form( false, options, frame )
local r = form( false, options, frame )
if not r then
if not r then
options = { mandatory = fill( frame.args.all ),
options = { mandatory = fill( frame.args.all ),
optional = fill( frame.args.opt ),
optional = fill( frame.args.opt ),
cat = frame.args.cat,
cat = frame.args.cat,
errNS = frame.args.errNS,
errNS = frame.args.errNS,
low = frame.args.low,
low = frame.args.low,
format = frame.args.format,
format = frame.args.format,
preview = frame.args.preview,
preview = frame.args.preview,
template = frame.args.template
template = frame.args.template
}
}
r = form( true, options, frame )
r = form( true, options, frame )
end
end
return r or ""
return r or ""
end -- p.check()
end -- .check()






function p.count( frame )
function p.count( frame )
-- Count number of template parameters
-- Count number of template parameters
-- Postcondition:
-- Postcondition:
-- Return string with digits including "0"
-- Return string with digits including "0"
-- Uses:
-- Uses:
-- TemplatePar.count()
-- TemplatePar.count()
return tostring( TemplatePar.count() )
return tostring( TemplatePar.count() )
end -- p.count()
end -- .count()






function p.countNotEmpty( frame )
function p.countNotEmpty( frame )
-- Count number of template parameters which are not empty
-- Count number of template parameters which are not empty
-- Postcondition:
-- Postcondition:
-- Return string with digits including "0"
-- Return string with digits including "0"
-- Uses:
-- Uses:
-- TemplatePar.countNotEmpty()
-- TemplatePar.countNotEmpty()
return tostring( TemplatePar.countNotEmpty() )
return tostring( TemplatePar.countNotEmpty() )
end -- p.countNotEmpty()
end -- .countNotEmpty()






function p.match( frame )
function p.match( frame )
-- Combined analysis of parameters and their values
-- Combined analysis of parameters and their values
-- Postcondition:
-- Precondition:
-- Return string with error message or ""
-- frame -- object; #invoke environment
-- Uses:
-- Postcondition:
-- mw.text.trim()
-- Return string with error message or ""
-- mw.ustring.lower()
-- Uses:
-- failure()
-- TemplatePar.framing()
-- form()
-- mw.text.trim()
-- TemplatePar.downcase()
-- mw.ustring.lower()
-- failure()
-- figure()
-- form()
-- feasible()
-- fault()
-- TemplatePar.downcase()
-- figure()
-- finalize()
local r = false
-- feasible()
local options = { cat = frame.args.cat,
-- fault()
errNS = frame.args.errNS,
-- finalize()
low = frame.args.low,
local r = false
local options = { cat = frame.args.cat,
format = frame.args.format,
errNS = frame.args.errNS,
preview = frame.args.preview,
low = frame.args.low,
template = frame.args.template
}
format = frame.args.format,
local k, v, s
preview = frame.args.preview,
local params = { }
template = frame.args.template
for k, v in pairs( frame.args ) do
}
if type( k ) == "number" then
local k, v, s
s, v = v:match( "^ *([^=]+) *= *(%S.*%S*) *$" )
local params = { }
if s then
TemplatePar.framing( frame )
s = mw.text.trim( s )
for k, v in pairs( frame.args ) do
if type( k ) == "number" then
if s == "" then
s = false
s, v = v:match( "^ *([^=]+) *= *(%S.*%S*) *$" )
end
if s then
end
s = mw.text.trim( s )
if s == "" then
if s then
if options.low then
s = false
s = mw.ustring.lower( s )
end
end
end
if s then
if params[ s ] then
s = params[ s ]
if options.low then
s[ #s + 1 ] = v
s = mw.ustring.lower( s )
else
end
if params[ s ] then
params[ s ] = { v }
end
s = params[ s ]
else
s[ #s + 1 ] = v
r = failure( "invalidPar", tostring( k ), options )
else
break -- for k, v
params[ s ] = { v }
end
end
end
else
end -- for k, v
r = failure( "invalidPar", tostring( k ), options )
if not r then
break -- for k, v
s = { }
end
end
for k, v in pairs( params ) do
end -- for k, v
s[ #s + 1 ] = k
end -- for k, v
if not r then
options.optional = s
s = { }
r = form( true, options, frame )
for k, v in pairs( params ) do
end
s[ #s + 1 ] = k
if not r then
end -- for k, v
local errMiss, errValues, lack, rule
options.optional = s
local targs = frame:getParent().args
r = form( true, options, frame )
options.optional = nil
end
if not r then
if options.low then
targs = TemplatePar.downcase()
local errMiss, errValues, lack, rule
else
local targs = frame:getParent().args
targs = frame:getParent().args
options.optional = nil
end
if options.low then
errMiss = false
targs = TemplatePar.downcase()
errValues = false
else
for k, v in pairs( params ) do
targs = frame:getParent().args
options.say = k
end
errMiss = false
errValue = false
s = targs[ k ]
errValues = false
if s then
for k, v in pairs( params ) do
if s == "" then
options.say = k
lack = true
s = targs[ k ]
else
if s then
lack = false
if s == "" then
end
lack = true
else
else
s = ""
lack = false
lack = true
end
end
else
for r, rule in pairs( v ) do
s = ""
options = figure( rule, options )
lack = true
r = feasible( s, options, true )
end
if r then
for r, rule in pairs( v ) do
if lack then
options = figure( rule, options )
if errMiss then
r = feasible( s, options, true )
errMiss = string.format( "%s, '%s'",
if r then
errMiss, k )
if lack then
else
if errMiss then
errMiss = string.format( "'%s'", k )
s = "%s, &quot;%s&quot;"
end
errMiss = string.format( s, errMiss, k )
elseif not errMiss then
else
errValues = fault( errValues, r )
errMiss = string.format( "&quot;%s&quot;",
end
k )
break -- for r, rule
end
end
elseif not errMiss then
end -- for s, rule
errValues = fault( errValues, r )
end -- for k, v
end
r = ( errMiss or errValues )
break -- for r, rule
if r then
end
if errMiss then
end -- for s, rule
r = failure( "undefined", errMiss, options )
end -- for k, v
else
r = ( errMiss or errValues )
r = failure( "invalid", errValues, options )
if r then
end
if errMiss then
r = finalize( r, options, frame )
r = failure( "undefined", errMiss, options )
end
else
end
r = failure( "invalid", errValues, options )
return r or ""
end
end -- .match()
r = finalize( r, options )
end
end
return r or ""
end -- p.match()






function p.valid( frame )
function p.valid( frame )
-- Check validity of one particular template parameter
-- Check validity of one particular template parameter
-- Precondition:
-- Precondition:
-- frame -- object; #invoke environment
-- frame -- object; #invoke environment
-- Postcondition:
-- Postcondition:
-- Return string with error message or ""
-- Return string with error message or ""
-- Uses:
-- Uses:
-- furnish()
-- furnish()
return furnish( frame, "valid" )
return furnish( frame, "valid" )
end -- p.valid()
end -- .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






function p.TemplatePar()
function p.TemplatePar()
-- Retrieve function access for modules
-- Retrieve function access for modules
-- Postcondition:
-- Postcondition:
-- Return table with functions
-- Return table with functions
return TemplatePar
return TemplatePar
end -- p.TemplatePar()
end -- .TemplatePar()





setmetatable( p, { __call = function ( func, ... )
setmetatable( p, nil )
return Failsafe
end } )


return p
return p