update from global upstream version, as requested by the developer; pre-view tested
細 (1版) |
bsd>Leyo (update from global upstream version, as requested by the developer; pre-view tested) |
||
| 1行目: | 1行目: | ||
-- | local TemplatePar = { serial = "2023-03-20", | ||
suite = "TemplatePar", | |||
item = 15393417, | |||
globals = { DateTime = 20652535, | |||
FileMedia = 24765326, | |||
Multilingual = 47541920, | |||
TemplUtl = 52364930, | |||
URLutil = 10859193 } } | |||
--[=[ | |||
Template parameter utility | Template parameter utility | ||
* assert | * assert | ||
| 6行目: | 14行目: | ||
* 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.messagePrefix = "lua-module-TemplatePar-" | |||
Local.L10nDef = {} | |||
Local.L10nDef.en = { | |||
L10nDef.en = { | |||
badPattern = "#invoke:TemplatePar pattern syntax error", | badPattern = "#invoke:TemplatePar pattern syntax error", | ||
dupOpt = "#invoke:TemplatePar repeated optional parameter", | dupOpt = "#invoke:TemplatePar repeated optional parameter", | ||
| 33行目: | 47行目: | ||
tooLong = "Error in template * parameter too long", | tooLong = "Error in template * parameter too long", | ||
tooShort = "Error in template * parameter too short", | tooShort = "Error in template * parameter too short", | ||
unavailable = "Error in template * parameter name missing", | |||
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 = { | |||
[ "ASCII" ] = "^[ -~]*$", | [ "ASCII" ] = "^[ -~]*$", | ||
[ "ASCII+" ] = "^[ -~]+$", | [ "ASCII+" ] = "^[ -~]+$", | ||
| 90行目: | 87行目: | ||
[ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%c-%c%c]+$", | [ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%c-%c%c]+$", | ||
1, 31, 127 ), | 1, 31, 127 ), | ||
[ "ref" ] = string.format( "%c'%c`UNIQ%s%sref%s%s%sQINU`%c'%c", | |||
127, 34, "%-", "%-", "%-", "%x+", | |||
"%-", 34, 127 ), | |||
[ "+" ] = "%S" | [ "+" ] = "%S" | ||
} | } | ||
local | 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 containsCJK( | 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? | -- Is any CJK character present? | ||
-- Precondition: | -- Precondition: | ||
-- | -- analyse -- string | ||
-- Postcondition: | -- Postcondition: | ||
-- Return false iff no CJK present | -- Return false iff no CJK present | ||
-- Uses: | -- Uses: | ||
-- >< patternCJK | -- >< Local.patternCJK | ||
-- mw.ustring.char() | -- mw.ustring.char() | ||
-- mw.ustring.match() | -- mw.ustring.match() | ||
local r = false | local r = false | ||
if not patternCJK then | if not Local.patternCJK then | ||
patternCJK = mw.ustring.char( 91, | Local.patternCJK = mw.ustring.char( 91, | ||
13312, 45, 40959, | 13312, 45, 40959, | ||
131072, 45, 178207, | 131072, 45, 178207, | ||
93 ) | 93 ) | ||
end | end | ||
if mw.ustring.match( | if mw.ustring.match( analyse, Local.patternCJK ) then | ||
r = true | r = true | ||
end | end | ||
| 138行目: | 232行目: | ||
-- Uses: | -- Uses: | ||
-- Module:FileMedia | -- Module:FileMedia | ||
-- Foreign() | |||
-- FileMedia.isFile() | |||
-- FileMedia.isType() | -- FileMedia.isType() | ||
local r | local r | ||
if attempt and attempt ~= "" then | if attempt and attempt ~= "" then | ||
local | local FileMedia = Foreign( "FileMedia" ) | ||
if type( FileMedia ) == " | if FileMedia and type( FileMedia.isFile ) == "function" | ||
and type( FileMedia.isType ) == "function" then | |||
local s, live = accept:match( "^([a-z]+)(:?)%+?$" ) | local s, live = accept:match( "^([a-z]+)(:?)%+?$" ) | ||
if live then | if live then | ||
| 180行目: | 276行目: | ||
-- Return some message string | -- Return some message string | ||
-- Uses: | -- Uses: | ||
-- > | -- > Local.messagePrefix | ||
-- > L10nDef | -- > Local.L10nDef | ||
-- mw.message.new() | |||
-- mw.language.getContentLanguage() | -- mw.language.getContentLanguage() | ||
-- | -- Module:Multilingual | ||
-- Foreign() | |||
local m = mw.message.new( | -- TemplatePar.framing() | ||
-- Multilingual.tabData() | |||
local m = mw.message.new( Local.messagePrefix .. say ) | |||
local r = false | local r = false | ||
if m:isBlank() then | if m:isBlank() then | ||
local l10n = L10nDef[ c ] | local c = mw.language.getContentLanguage():getCode() | ||
if | local l10n = Local.L10nDef[ c ] | ||
l10n = L10nDef[ | 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 | end | ||
else | else | ||
m:inLanguage( c ) | m:inLanguage( c ) | ||
| 205行目: | 316行目: | ||
local function | local function faculty( accept, attempt ) | ||
-- | -- Check string as possible boolean | ||
-- Precondition: | -- Precondition: | ||
-- | -- accept -- string; requirement | ||
-- | -- boolean | ||
-- boolean+ | |||
-- attempt -- string; to be tested | |||
-- Postcondition: | -- Postcondition: | ||
-- Return | -- Return error keyword, or false | ||
-- Uses: | -- Uses: | ||
-- mw. | -- Module:TemplUtl | ||
-- Foreign() | |||
end -- | -- 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() | |||
| 243行目: | 381行目: | ||
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() | |||
| 283行目: | 587行目: | ||
-- false if valid or no answer permitted | -- false if valid or no answer permitted | ||
-- Uses: | -- Uses: | ||
-- > | -- > Local.patterns | ||
-- failure() | -- failure() | ||
-- mw.text.trim() | -- mw.text.trim() | ||
-- faculty() | |||
-- fast() | |||
-- facility() | -- facility() | ||
-- | -- familiar() | ||
-- far() | |||
-- fair() | |||
-- containsCJK() | -- containsCJK() | ||
local r | local r = false | ||
local s | local s = false | ||
local show = nil | local show = nil | ||
local scan = false | local scan = false | ||
local stuff = mw.text.trim( analyze ) | |||
if type( options.pattern ) == "string" then | if type( options.pattern ) == "string" then | ||
if options.key then | if options.key then | ||
| 306行目: | 615行目: | ||
end | end | ||
if s ~= "*" then | if s ~= "*" then | ||
scan = | scan = Local.patterns[ s ] | ||
end | end | ||
if type( scan ) == "string" then | if type( scan ) == "string" then | ||
if s == "n" or s == "0,0" or s == "0.0" then | if s == "n" or s == "0,0" or s == "0.0" then | ||
if not | if not stuff:match( "[0-9]" ) and | ||
not | not stuff:match( "^%s*$" ) then | ||
scan = false | scan = false | ||
if options.say then | if options.say then | ||
show = string.format( " | show = string.format( ""%s"", options.say ) | ||
end | end | ||
if abbr then | if abbr then | ||
| 328行目: | 637行目: | ||
n = tonumber( n ) | n = tonumber( n ) | ||
if n then | if n then | ||
local i = tonumber( | local i = tonumber( stuff ) | ||
if i then | if i then | ||
if op == "<" then | if op == "<" then | ||
| 352行目: | 661行目: | ||
r = "undefined" | r = "undefined" | ||
end | 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 | elseif s:match( "^image%+?:?$" ) or | ||
s:match( "^file%+?:?$" ) then | s:match( "^file%+?:?$" ) then | ||
r = facility( s, | 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 | n = true | ||
end | end | ||
-- datetime+ | |||
-- iso8631+ | |||
-- line+ | |||
if not n and not r then | if not n and not r then | ||
r = "unknownRule" | r = "unknownRule" | ||
| 366行目: | 686行目: | ||
if r then | if r then | ||
if options.say then | if options.say then | ||
show = string.format( " | show = string.format( ""%s" %s", options.say, s ) | ||
else | else | ||
show = s | show = s | ||
| 379行目: | 699行目: | ||
end | end | ||
if scan then | if scan then | ||
local legal, got = pcall( | local legal, got = pcall( fair, stuff, scan ) | ||
if legal then | if legal then | ||
if not got then | if not got then | ||
if s == "aa" then | if s == "aa" then | ||
got = containsCJK( | got = containsCJK( stuff ) | ||
end | end | ||
if not got then | if not got then | ||
if options.say then | if options.say then | ||
show = string.format( " | show = string.format( ""%s"", options.say ) | ||
end | end | ||
if abbr then | if abbr then | ||
| 414行目: | 734行目: | ||
-- Postcondition: | -- Postcondition: | ||
-- Return true iff found | -- Return true iff found | ||
local k, v | local k, v, r | ||
for k, v in pairs( haystack ) do | for k, v in pairs( haystack ) do | ||
if k == needle then | if k == needle then | ||
r = true | |||
end | end | ||
end -- for k, v | end -- for k, v | ||
return false | return r or false | ||
end -- fed() | end -- fed() | ||
| 435行目: | 755行目: | ||
-- Uses: | -- Uses: | ||
-- TemplatePar.downcase() | -- TemplatePar.downcase() | ||
-- | -- TemplatePar.framing() | ||
-- frame:getParent() | -- frame:getParent() | ||
local g, k, v | local g, k, v | ||
| 442行目: | 762行目: | ||
g = TemplatePar.downcase( options ) | g = TemplatePar.downcase( options ) | ||
else | else | ||
g = | g = TemplatePar.framing() | ||
if light then | if light then | ||
g = g:getParent() | g = g:getParent() | ||
| 486行目: | 806行目: | ||
if type( sub ) == "string" then | if type( sub ) == "string" then | ||
sub = sub:gsub( "%%!", "|" ) | sub = sub:gsub( "%%!", "|" ) | ||
:gsub( "%%%(%(", "{{" ) | |||
:gsub( "%%%)%)", "}}" ) | |||
:gsub( "\\n", string.char( 10 ) ) | |||
options.pattern = sub | options.pattern = sub | ||
options.key = nil | options.key = nil | ||
| 526行目: | 847行目: | ||
local function finalize( submit, options | local function finalize( submit, options ) | ||
-- Finalize message | -- Finalize message | ||
-- Precondition: | -- Precondition: | ||
| 535行目: | 856行目: | ||
-- options.cat | -- options.cat | ||
-- options.template | -- options.template | ||
-- 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 learn = false | |||
local show = false | |||
local opt, s | local opt, s | ||
if type( options ) == "table" then | if type( options ) == "table" then | ||
opt = options | opt = options | ||
| 551行目: | 873行目: | ||
s = opt.preview | s = opt.preview | ||
if type( s ) == "string" and | if type( s ) == "string" and | ||
s ~= "" and s ~= "0" and s ~= "-" then | |||
local sniffer = "{{REVISIONID}}" | |||
if lazy then | if lazy then | ||
show = "" | show = "" | ||
lazy = false | lazy = false | ||
end | end | ||
if | if TemplatePar.framing():preprocess( sniffer ) == "" then | ||
if s == "1" then | if s == "1" then | ||
show = "*" | show = "*" | ||
| 565行目: | 885行目: | ||
show = s | show = s | ||
end | end | ||
learn = true | |||
end | end | ||
end | end | ||
| 581行目: | 902行目: | ||
local i | local i | ||
if not show or show == "*" then | 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( ">>>" ) | |||
sign = string.format( "[[#%s|%s]]", | |||
sign, tostring( btn ) ) | |||
top:wikitext( sign, " ", submit ) | |||
mw.addWarning( tostring( top ) ) | |||
end | |||
show = tostring( e ) | |||
end | end | ||
i = show:find( "@@@", 1, true ) | i = show:find( "@@@", 1, true ) | ||
| 593行目: | 935行目: | ||
r = show | r = show | ||
end | end | ||
end | |||
if learn and r then | |||
-- r = fatal( r ) | |||
end | end | ||
s = opt.cat | s = opt.cat | ||
if type( s ) == "string" then | if type( s ) == "string" then | ||
local link | |||
if opt.errNS then | if opt.errNS then | ||
local ns = mw.title.getCurrentTitle().namespace | local ns = mw.title.getCurrentTitle().namespace | ||
| 603行目: | 949行目: | ||
local spaces = string.format( " %s ", opt.errNS ) | local spaces = string.format( " %s ", opt.errNS ) | ||
if spaces:match( space ) then | if spaces:match( space ) then | ||
link = true | |||
end | end | ||
elseif st == "table" then | elseif st == "table" then | ||
for i = 1, #opt.errNS do | for i = 1, #opt.errNS do | ||
if opt.errNS[ i ] == ns then | if opt.errNS[ i ] == ns then | ||
link = true | |||
break -- for i | break -- for i | ||
end | end | ||
end -- for i | end -- for i | ||
end | end | ||
else | |||
link = true | |||
end | end | ||
if | if link then | ||
local cats, i | |||
if not r then | if not r then | ||
r = "" | r = "" | ||
| 625行目: | 972行目: | ||
end | end | ||
end | end | ||
cats = mw.text.split( s, "%s*#%s*" ) | |||
for i = 1, #cats do | for i = 1, #cats do | ||
s = mw.text.trim( cats[ i ] ) | s = mw.text.trim( cats[ i ] ) | ||
| 673行目: | 1,019行目: | ||
-- failure() | -- failure() | ||
-- fed() | -- fed() | ||
local r = false | local r = false | ||
local lack | |||
for k, v in pairs( got ) do | for k, v in pairs( got ) do | ||
if not finder( valid, k ) then | if k == "" then | ||
lack = true | |||
break -- for k, v | |||
elseif not finder( valid, k ) then | |||
r = fault( r, k ) | r = fault( r, k ) | ||
end | end | ||
end -- for k, v | end -- for k, v | ||
if r then | if lack then | ||
r = failure( "unavailable", false, options ) | |||
elseif r then | |||
r = failure( "unknown", | r = failure( "unknown", | ||
string.format( " | string.format( ""%s"", r ), | ||
options ) | options ) | ||
else -- all names valid | else -- all names valid | ||
| 804行目: | 1,155行目: | ||
-- options.mandatory | -- options.mandatory | ||
-- options.optional | -- options.optional | ||
-- frame -- object, or false | -- frame -- object; #invoke environment, 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: | ||
-- TemplatePar.framing() | |||
-- fold() | -- fold() | ||
-- fetch() | -- fetch() | ||
| 814行目: | 1,166行目: | ||
-- finalize() | -- finalize() | ||
local duty, r | local duty, r | ||
if frame then | |||
TemplatePar.framing( frame ) | |||
end | |||
if type( options ) == "table" then | if type( options ) == "table" then | ||
if type( options.mandatory ) ~= "table" then | if type( options.mandatory ) ~= "table" then | ||
| 836行目: | 1,191行目: | ||
end | end | ||
end | end | ||
return finalize( r, options | return finalize( r, options ) | ||
end -- form() | end -- form() | ||
| 871行目: | 1,226行目: | ||
show = " <" .. options.min | show = " <" .. options.min | ||
if options.say then | if options.say then | ||
show = string.format( "%s | show = string.format( "%s "%s"", show, options.say ) | ||
end | end | ||
r = failure( "tooShort", show, options ) | r = failure( "tooShort", show, options ) | ||
| 884行目: | 1,239行目: | ||
show = " >" .. options.max | show = " >" .. options.max | ||
if options.say then | if options.say then | ||
show = string.format( "%s | show = string.format( "%s "%s"", show, options.say ) | ||
end | end | ||
r = failure( "tooLong", show, options ) | r = failure( "tooLong", show, options ) | ||
| 907行目: | 1,262行目: | ||
-- 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() | ||
| 914行目: | 1,269行目: | ||
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 | ||
| 986行目: | 1,341行目: | ||
end | end | ||
if r then | if r then | ||
r = finalize( r, options | r = finalize( r, options ) | ||
else | else | ||
s = frame.args[ 1 ] or "" | s = frame.args[ 1 ] or "" | ||
| 994行目: | 1,349行目: | ||
end | end | ||
if action == "valid" then | if action == "valid" then | ||
r = TemplatePar.valid( s, options | r = TemplatePar.valid( s, options ) | ||
elseif action == "assert" then | elseif action == "assert" then | ||
r = TemplatePar.assert( s, "", options ) | r = TemplatePar.assert( s, "", options ) | ||
| 1,021行目: | 1,376行目: | ||
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 | ||
| 1,104行目: | 1,459行目: | ||
TemplatePar.valid = function ( access, options | TemplatePar.valid = function ( access, options ) | ||
-- Check validity of one particular template parameter | -- Check validity of one particular template parameter | ||
-- Precondition: | -- Precondition: | ||
| 1,110行目: | 1,465行目: | ||
-- string or number | -- string or number | ||
-- 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; | ||
| 1,117行目: | 1,471行目: | ||
-- mw.text.trim() | -- mw.text.trim() | ||
-- TemplatePar.downcase() | -- TemplatePar.downcase() | ||
-- TemplatePar.framing() | |||
-- frame:getParent() | -- frame:getParent() | ||
-- formatted() | -- formatted() | ||
| 1,140行目: | 1,495行目: | ||
params = TemplatePar.downcase( options ) | params = TemplatePar.downcase( options ) | ||
else | else | ||
params = | params = TemplatePar.framing():getParent() | ||
end | end | ||
r = formatted( params, access, options ) | r = formatted( params, access, options ) | ||
| 1,146行目: | 1,501行目: | ||
r = failure( "noname", false, options ) | r = failure( "noname", false, options ) | ||
end | end | ||
return finalize( r, options | return finalize( r, options ) | ||
end -- TemplatePar.valid() | end -- TemplatePar.valid() | ||
| 1,162行目: | 1,517行目: | ||
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() | |||
| 1,179行目: | 1,617行目: | ||
-- furnish() | -- furnish() | ||
return furnish( frame, "assert" ) | return furnish( frame, "assert" ) | ||
end -- .assert() | end -- p.assert() | ||
| 1,216行目: | 1,654行目: | ||
end | end | ||
return r or "" | return r or "" | ||
end -- .check() | end -- p.check() | ||
| 1,227行目: | 1,665行目: | ||
-- TemplatePar.count() | -- TemplatePar.count() | ||
return tostring( TemplatePar.count() ) | return tostring( TemplatePar.count() ) | ||
end -- .count() | end -- p.count() | ||
| 1,238行目: | 1,676行目: | ||
-- TemplatePar.countNotEmpty() | -- TemplatePar.countNotEmpty() | ||
return tostring( TemplatePar.countNotEmpty() ) | return tostring( TemplatePar.countNotEmpty() ) | ||
end -- .countNotEmpty() | end -- p.countNotEmpty() | ||
| 1,244行目: | 1,682行目: | ||
function p.match( frame ) | function p.match( frame ) | ||
-- Combined analysis of parameters and their values | -- Combined analysis of parameters and their values | ||
-- Precondition: | |||
-- frame -- object; #invoke environment | |||
-- Postcondition: | -- Postcondition: | ||
-- Return string with error message or "" | -- Return string with error message or "" | ||
-- Uses: | -- Uses: | ||
-- TemplatePar.framing() | |||
-- mw.text.trim() | -- mw.text.trim() | ||
-- mw.ustring.lower() | -- mw.ustring.lower() | ||
| 1,266行目: | 1,707行目: | ||
local k, v, s | local k, v, s | ||
local params = { } | local params = { } | ||
TemplatePar.framing( frame ) | |||
for k, v in pairs( frame.args ) do | for k, v in pairs( frame.args ) do | ||
if type( k ) == "number" then | if type( k ) == "number" then | ||
| 1,312行目: | 1,754行目: | ||
for k, v in pairs( params ) do | for k, v in pairs( params ) do | ||
options.say = k | options.say = k | ||
s = targs[ k ] | |||
s = targs[ k ] | |||
if s then | if s then | ||
if s == "" then | if s == "" then | ||
| 1,330行目: | 1,771行目: | ||
if lack then | if lack then | ||
if errMiss then | if errMiss then | ||
s = "%s, "%s"" | |||
errMiss = string.format( s, errMiss, k ) | |||
else | else | ||
errMiss = string.format( " | errMiss = string.format( ""%s"", | ||
k ) | |||
end | end | ||
elseif not errMiss then | elseif not errMiss then | ||
| 1,349行目: | 1,791行目: | ||
r = failure( "invalid", errValues, options ) | r = failure( "invalid", errValues, options ) | ||
end | end | ||
r = finalize( r, options | r = finalize( r, options ) | ||
end | end | ||
end | end | ||
return r or "" | return r or "" | ||
end -- .match() | end -- p.match() | ||
| 1,366行目: | 1,808行目: | ||
-- furnish() | -- furnish() | ||
return furnish( frame, "valid" ) | return furnish( frame, "valid" ) | ||
end -- .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 | |||
| 1,375行目: | 1,837行目: | ||
-- Return table with functions | -- Return table with functions | ||
return TemplatePar | return TemplatePar | ||
end -- .TemplatePar() | end -- p.TemplatePar() | ||
setmetatable( p, { __call = function ( func, ... ) | |||
setmetatable( p, nil ) | |||
return Failsafe | |||
end } ) | |||
return p | return p | ||