templatepar

Dokumentasi untuk modul ini dapat dibuat di Modul:TemplatePar/doc

local TemplatePar = { serial  = "2023-03-20",                       suite   = "TemplatePar",                       item    = 15393417,                       globals = { DateTime     = 20652535,                                   FileMedia    = 24765326,                                   Multilingual = 47541920,                                   TemplUtl     = 52364930,                                   URLutil      = 10859193 } } --[=[ Template parameter utility * assert * check * count * countNotEmpty * downcase() * duplicates * match * valid * verify() * TemplatePar() * failsafe() ]=]   local Local     = { frame = false } local Failsafe  = TemplatePar local GlobalMod = Local    -- Module globals Local.messagePrefix = "lua-module-TemplatePar-" Local.L10nDef = {} Local.L10nDef.en = {     badPattern  = "#invoke:TemplatePar pattern syntax error",     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",     invalidPar  = "#invoke:TemplatePar invalid parameter",     minmax      = "#invoke:TemplatePar min > max",     missing     = "#invoke:TemplatePar missing library",     multiSpell  = "Error in template * multiple spelling of parameter",     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",     tooShort    = "Error in template * parameter too short",     unavailable = "Error in template * parameter name missing",     undefined   = "Error in template * mandatory parameter missing",     unknown     = "Error in template * unknown parameter name",     unknownRule = "#invoke:TemplatePar unknown rule" } Local.patterns = {     [ "ASCII" ]    = "^[ -~]*$",     [ "ASCII+" ]   = "^[ -~]+$",     [ "ASCII+1" ]  = "^[!-~]+$",     [ "n" ]        = "^[%-]?[0-9]*$",     [ "n>0" ]      = "^[0-9]*[1-9][0-9]*$",     [ "N+" ]       = "^[%-]?[1-9][0-9]*$",     [ "N>0" ]      = "^[1-9][0-9]*$",     [ "x" ]        = "^[0-9A-Fa-f]*$",     [ "x+" ]       = "^[0-9A-Fa-f]+$",     [ "X" ]        = "^[0-9A-F]*$",     [ "X+" ]       = "^[0-9A-F]+$",     [ "0,0" ]      = "^[%-]?[0-9]*,?[0-9]*$",     [ "0,0+" ]     = "^[%-]?[0-9]+,[0-9]+$",     [ "0,0+?" ]    = "^[%-]?[0-9]+,?[0-9]*$",     [ "0.0" ]      = "^[%-]?[0-9]*[%.]?[0-9]*$",     [ "0.0+" ]     = "^[%-]?[0-9]+%.[0-9]+$",     [ "0.0+?" ]    = "^[%-]?[0-9]+[%.]?[0-9]*$",     [ ".0+" ]      = "^[%-]?[0-9]*[%.]?[0-9]+$",     [ "ID" ]       = "^[A-Za-z]?[A-Za-z_0-9]*$",     [ "ID+" ]      = "^[A-Za-z][A-Za-z_0-9]*$",     [ "ABC" ]      = "^[A-Z]*$",     [ "ABC+" ]     = "^[A-Z]+$",     [ "Abc" ]      = "^[A-Z]*[a-z]*$",     [ "Abc+" ]     = "^[A-Z][a-z]+$",     [ "abc" ]      = "^[a-z]*$",     [ "abc+" ]     = "^[a-z]+$",     [ "aBc+" ]     = "^[a-z]+[A-Z][A-Za-z]*$",     [ "w" ]        = "^%S*$",     [ "w+" ]       = "^%S+$",     [ "base64" ]   = "^[A-Za-z0-9%+/]*$",     [ "base64+" ]  = "^[A-Za-z0-9%+/]+$",     [ "aa" ]       = "[%a%a].*[%a%a]",     [ "pagename" ] = string.format( "^[^#<>%%[%%]|{}%c-%c%c]+$",                                     1, 31, 127 ),     [ "ref" ]      = string.format( "%c'%c`UNIQ%s%sref%s%s%sQINU`%c'%c",                                     127, 34, "%-", "%-", "%-", "%x+",                                     "%-", 34, 127 ),     [ "+" ]        = "%S" } Local.boolean = { ["1"]     = true,                   ["true"]  = true,                   y         = true,                   yes       = true,                   on        = true,                   ["0"]     = true,                   ["false"] = true,                   ["-"]     = true,                   n         = true,                   no        = true,                   off       = true } Local.patternCJK = false    local foreignModule = function ( access, advanced, append, alt, alert )     -- Fetch global module     -- Precondition:     --     access    -- string, with name of base module     --     advanced  -- true, for require(); else mw.loadData()     --     append    -- string, with subpage part, if any; or false     --     alt       -- number, of wikidata item of root; or false     --     alert     -- true, for throwing error on data problem     -- Postcondition:     --     Returns whatever, probably table     -- 2020-01-01     local storage = access     local finer = function ()                       if append then                           storage = string.format( "%s/%s",                                                    storage,                                                    append )                       end                   end     local fun, lucky, r, suited     if advanced then         fun = require     else         fun = mw.loadData     end     GlobalMod.globalModules = GlobalMod.globalModules or { }     suited = GlobalMod.globalModules[ access ]     if not suited then         finer()         lucky, r = pcall( fun,  "Module:" .. storage )     end     if not lucky then         if not suited  and            type( alt ) == "number"  and            alt > 0 then             suited = string.format( "Q%d", alt )             suited = mw.wikibase.getSitelink( suited )             GlobalMod.globalModules[ access ] = suited or true         end         if type( suited ) == "string" then             storage = suited             finer()             lucky, r = pcall( fun, storage )         end         if not lucky and alert then             error( "Missing or invalid page: " .. storage )         end     end     return r end -- foreignModule()    local function Foreign( access  )     -- Access standardized library     -- Precondition:     --     access  -- string, with name of base module     -- Postcondition:     --     Return library table, or not     -- Uses:     local r     if Local[ access ] then         r = Local[ access ]     else         local bib = foreignModule( access,                                    true,                                    false,                                    TemplatePar.globals[ access ],                                    false )         if type( bib ) == "table"   and            type( bib[ access ] ) == "function" then             bib = bib[ access ]()             if type( bib ) == "table" then                 r               = bib                 Local[ access ] = bib             end         end     end     return r end -- Foreign()    local function containsCJK( analyse )     -- Is any CJK character present?     -- Precondition:     --     analyse  -- string     -- Postcondition:     --     Return false iff no CJK present     -- Uses:     --     >< Local.patternCJK     --     mw.ustring.char()     --     mw.ustring.match()     local r = false     if not Local.patternCJK then         Local.patternCJK = mw.ustring.char( 91,                                        13312, 45,  40959,                                       131072, 45, 178207,                                       93 )     end     if mw.ustring.match( analyse, Local.patternCJK ) then         r = true     end     return r end -- containsCJK()    local function facility( accept, attempt )     -- Check string as possible file name or other source page     -- Precondition:     --     accept   -- string; requirement     --                         file     --                         file+     --                         file:     --                         file:+     --                         image     --                         image+     --                         image:     --                         image:+     --     attempt  -- string; to be tested     -- Postcondition:     --     Return error keyword, or false     -- Uses:     --     Module:FileMedia     --     Foreign()     --     FileMedia.isFile()     --     FileMedia.isType()     local r     if attempt and attempt ~= "" then         local FileMedia = Foreign( "FileMedia" )         if FileMedia  and  type( FileMedia.isFile ) == "function"                       and  type( FileMedia.isType ) == "function" then             local s, live = accept:match( "^([a-z]+)(:?)%+?$" )             if live then                 if FileMedia.isType( attempt, s ) then                     if FileMedia.isFile( attempt ) then                         r = false                     else                         r = "notFound"                     end                 else                     r = "invalid"                 end             elseif FileMedia.isType( attempt, s ) then                 r = false             else                 r = "invalid"             end         else             r = "missing"         end     elseif accept:match( "%+$" ) then         r = "empty"     else         r = false     end     return r end -- facility()    local function factory( say )     -- Retrieve localized message string in content language     -- Precondition:     --     say  -- string; message ID     -- Postcondition:     --     Return some message string     -- Uses:     --     >  Local.messagePrefix     --     >  Local.L10nDef     --     mw.message.new()     --     mw.language.getContentLanguage()     --     Module:Multilingual     --     Foreign()     --     TemplatePar.framing()     --     Multilingual.tabData()     local m = mw.message.new( Local.messagePrefix .. say )     local r = false     if m:isBlank() then         local c = mw.language.getContentLanguage():getCode()         local l10n = Local.L10nDef[ c ]         if l10n then             r = l10n[ say ]         else             local MultiL = Foreign( "Multilingual" )             if MultiL  and  type( MultiL.tabData ) == "function" then                 local lang                 r, lang = MultiL.tabData( "I18n/Module:TemplatePar",                                           say,                                           false,                                           TemplatePar.framing() )             end         end         if not r then             r = Local.L10nDef.en[ say ]         end     else         m:inLanguage( c )         r = m:plain()     end     if not r then         r = string.format( "(((%s)))", say )     end     return r end -- factory()    local function faculty( accept, attempt )     -- Check string as possible boolean     -- Precondition:     --     accept   -- string; requirement     --                         boolean     --                         boolean+     --     attempt  -- string; to be tested     -- Postcondition:     --     Return error keyword, or false     -- Uses:     --     Module:TemplUtl     --     Foreign()     --     TemplUtl.faculty()     local r     r = mw.text.trim( attempt ):lower()     if r == "" then         if accept == "boolean+" then             r = "empty"         else             r = false         end     elseif Local.boolean[ r ]  or   r:match( "^[01%-]+$" ) then         r = false     else         local TemplUtl = Foreign( "TemplUtl" )         if TemplUtl  and  type( TemplUtl.faculty ) == "function" then             r = TemplUtl.faculty( r, "-" )             if r == "-" then                 r = "invalid"             else                 r = false             end         else             r = "invalid"         end     end     return r end -- faculty()    local function failure( spec, suspect, options )     -- Submit localized error message     -- Precondition:     --     spec     -- string; message ID     --     suspect  -- string or nil; additional information     --     options  -- table or nil; optional details     --                 options.template     -- Postcondition:     --     Return string     -- Uses:     --     factory()     local r = factory( spec )     if type( options ) == "table" then         if type( options.template ) == "string" then             if #options.template > 0 then                 r = string.format( "%s (%s)", r, options.template )             end         end     end     if suspect then         r = string.format( "%s: %s", r, suspect )     end     return r end -- failure()    local function fair( story, scan )     -- Test for match (possibly user-defined with syntax error)     -- Precondition:     --     story  -- string; parameter value     --     scan   -- string; pattern     -- Postcondition:     --     Return nil, if not matching, else non-nil     -- Uses:     --     mw.ustring.match()     return  mw.ustring.match( story, scan ) end -- fair()    local function familiar( accept, attempt )     -- Check string as possible language name or list     -- Precondition:     --     accept   -- string; requirement     --                         lang     --                         langs     --                         langW     --                         langsW     --                         lang+     --                         langs+     --                         langW+     --                         langsW+     --     attempt  -- string; to be tested     -- Postcondition:     --     Return error keyword, or false     -- Uses:     --     Module:Multilingual     --     Foreign()     --     Multilingual.isLang()     local r     if attempt and attempt ~= "" then         local MultiL = Foreign( "Multilingual" )         if MultiL  and  type( MultiL.isLang ) == "function" then             local lazy = accept:find( "W", 1, true )             if accept:find( "s", 1, true ) then                 local group = mw.text.split( attempt, "%s+" )                 r = false                 for i = 1, #group do                     if not MultiL.isLang( group[ i ], lazy ) then                         r = "invalid"                         break -- for i                     end                 end -- for i             elseif MultiL.isLang( attempt, lazy ) then                 r = false             else                 r = "invalid"             end         else             r = "missing"         end     elseif accept:find( "+", 1, true ) then         r = "empty"     else         r = false     end     return r end -- familiar()    local function far( accept, attempt )     -- Check string as possible URL     -- Precondition:     --     accept   -- string; requirement     --                         url     --                         url+     --     attempt  -- string; to be tested     -- Postcondition:     --     Return error keyword, or false     -- Uses:     --     Module:URLutil     --     Foreign()     --     URLutil.isWebURL()     local r     if attempt and attempt ~= "" then         local URLutil = Foreign( "URLutil" )         if URLutil  and  type( URLutil.isWebURL ) == "function" then             if URLutil.isWebURL( attempt ) then                 r = false             else                 r = "invalid"             end         else             r = "missing"         end     elseif accept:find( "+", 1, true ) then         r = "empty"     else         r = false     end     return r end -- far()    local function fast( accept, attempt )     -- Check string as possible date or time     -- Precondition:     --     accept   -- string; requirement     --                         datetime     --                         datetime+     --                         datetime/y     --                         datetime/y+     --                         datetime/ym     --                         datetime/ym+     --                         datetime/ymd     --                         datetime/ymd+     --     attempt  -- string; to be tested     -- Postcondition:     --     Return error keyword, or false     -- Uses:     --     Module:DateTime     --     Foreign()     --     DateTime.DateTime()     local r     r = mw.text.trim( attempt )     if r == "" then         if accept:find( "+", 1, true ) then             r = "empty"         else             r = false         end     else         local DateTime = Foreign( "DateTime" )         if type( DateTime ) == "table" then             local d = DateTime( attempt )             if type( d ) == "table" then                 if accept:find( "/", 1, true ) then                     r = "invalid"                     if accept:sub( 1, 10 ) == "datetime/y" then                         if d.year then                             r = false                             if accept:sub( 1, 11 ) == "datetime/ym" then                                 if d.month then                                     if accept:sub( 1, 12 )                                                    == "datetime/ymd" then                                         if not d.dom then                                             r = "invalid"                                         end                                     end                                 else                                     r = "invalid"                                 end                             end                         end                     end                 else                     r = false                 end             else                 r = "invalid"             end         else             r = "invalid"         end     end     return r end -- fast()    local function fault( store, key )     -- Add key to collection string and insert separator     -- Precondition:     --     store  -- string or nil or false; collection string     --     key    -- string or number; to be appended     -- Postcondition:     --     Return string; extended     local r     local s     if type( key ) == "number" then         s = tostring( key )     else         s = key     end     if store then         r = string.format( "%s; %s", store, s )     else         r = s     end     return r end -- fault()    local function feasible( analyze, options, abbr )     -- Check content of a value     -- Precondition:     --     analyze  -- string to be analyzed     --     options  -- table or nil; optional details     --                 options.pattern     --                 options.key     --                 options.say     --     abbr     -- true: abbreviated error message     -- Postcondition:     --     Return string with error message as configured;     --            false if valid or no answer permitted     -- Uses:     --     >  Local.patterns     --     failure()     --     mw.text.trim()     --     faculty()     --     fast()     --     facility()     --     familiar()     --     far()     --     fair()     --     containsCJK()     local r     = false     local s     = false     local show  = nil     local scan  = false     local stuff = mw.text.trim( analyze )     if type( options.pattern ) == "string" then         if options.key then             r = failure( "dupRule", false, options )         else             scan = options.pattern         end     else         if type( options.key ) == "string" then             s = mw.text.trim( options.key )         else             s = "+"         end         if s ~= "*" then             scan = Local.patterns[ s ]         end         if type( scan ) == "string" then             if s == "n" or s == "0,0" or s == "0.0" then                 if not stuff:match( "[0-9]" )  and                    not stuff:match( "^%s*$" ) then                     scan = false                     if options.say then                         show = string.format( ""%s"", options.say )                     end                     if abbr then                         r = show                     else                         r = failure( "invalid", show, options )                     end                 end             end         elseif s ~= "*" then             local op, n, plus = s:match( "([]=?)([-0-9][%S]*)(+?)" )             if op then                 n = tonumber( n )                 if n then                     local i = tonumber( stuff )                     if i then                         if op == "<" then                             i = ( i < n )                         elseif op == "<=" then                             i = ( i <= n )                         elseif op == ">" then                             i = ( i > n )                         elseif op == ">=" then                             i = ( i >= n )                         elseif op == "==" then                             i = ( i == n )                         elseif op == "!=" then                             i = ( i ~= n )                         else                             n = false                         end                     end                     if not i then                         r = "invalid"                     end                 elseif plus then                     r = "undefined"                 end             elseif s:match( "^boolean%+?$" ) then                 r = faculty( s, stuff )                 n = true             elseif s:match( "^datetime/?y?m?d?%+?$" ) then                 r = fast( s, stuff )                 n = true             elseif s:match( "^image%+?:?$" )  or                    s:match( "^file%+?:?$" ) then                 r = facility( s, stuff )                 n = true             elseif s:match( "langs?W?%+?" ) then                 r = familiar( s, stuff )                 n = true             elseif s:match( "url%+?" ) then                 r = far( s, stuff )                 n = true             end -- datetime+ -- iso8631+ -- line+             if not n and not r then                 r = "unknownRule"             end             if r then                 if options.say then                     show = string.format( ""%s" %s", options.say, s )                 else                     show = s                 end                 if abbr then                     r = show                 else                     r = failure( r, show, options )                 end             end         end     end     if scan then         local legal, got = pcall( fair, stuff, scan )         if legal then             if not got then                 if s == "aa" then                     got = containsCJK( stuff )                 end                 if not got then                     if options.say then                         show = string.format( ""%s"", 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()    local function fed( haystack, needle )     -- Find needle in haystack map     -- Precondition:     --     haystack  -- table; map of key values     --     needle    -- any; identifier     -- Postcondition:     --     Return true iff found     local k, v, r     for k, v in pairs( haystack ) do         if k == needle then             r = true         end     end -- for k, v     return r or false end -- fed()    local function fetch( light, options )     -- Return regular table with all parameters     -- Precondition:     --     light    -- true: template transclusion;  false: #invoke     --     options  -- table; optional details     --                 options.low     -- Postcondition:     --     Return table; whitespace-only values as false     -- Uses:     --     TemplatePar.downcase()     --     TemplatePar.framing()     --     frame:getParent()     local g, k, v     local r = { }     if options.low then         g = TemplatePar.downcase( options )     else         g = TemplatePar.framing()         if light then             g = g:getParent()         end         g = g.args     end     if type( g ) == "table"  then         r = { }         for k, v in pairs( g ) do             if type( v ) == "string" then                 if v:match( "^%s*$" ) then                     v = false                 end             else                 v = false             end             if type( k ) == "number" then                 k = tostring( k )             end             r[ k ] = v         end -- for k, v     else         r = g     end     return r end -- fetch()    local function figure( append, options )     -- Extend options by rule from #invoke strings     -- Precondition:     --     append   -- string or nil; requested rule     --     options  --  table; details     --                  ++ .key     --                  ++ .pattern     -- Postcondition:     --     Return sequence table     local r = options     if type( append ) == "string" then         local story = mw.text.trim( append )         local sub   = story:match( "^/(.*%S)/$" )         if type( sub ) == "string" then             sub             = sub:gsub( "%%!", "|" )                                  :gsub( "%%%(%(", "{{" )                                  :gsub( "%%%)%)", "}}" )                                  :gsub( "\\n", string.char( 10 ) )             options.pattern = sub             options.key     = nil         else             options.key     = story             options.pattern = nil         end     end     return r end -- figure()    local function fill( specified )     -- Split requirement string separated by '='     -- Precondition:     --     specified  -- string or nil; requested parameter set     -- Postcondition:     --     Return sequence table     -- Uses:     --     mw.text.split()     local r     if specified then         local i, s         r = mw.text.split( specified, "%s*=%s*" )         for i = #r, 1, -1 do             s = r[ i ]             if #s == 0 then                 table.remove( r, i )             end         end -- for i, -1     else         r = { }     end     return r end -- fill()    local function finalize( submit, options )     -- Finalize message     -- Precondition:     --     submit   -- string or false or nil; non-empty error message     --     options  -- table or nil; optional details     --                 options.format     --                 options.preview     --                 options.cat     --                 options.template     -- Postcondition:     --     Return string or false     -- Uses:     --     TemplatePar.framing()     --     factory()     local r = false     if submit then         local lazy  = false         local learn = false         local show  = false         local opt, s         if type( options ) == "table" then             opt  = options             show = opt.format             lazy = ( show == ""  or  show == "0"  or  show == "-" )             s    = opt.preview             if type( s ) == "string"  and                s ~= ""  and  s ~= "0"  and  s ~= "-" then                 local sniffer = "{{REVISIONID}}"                 if lazy then                     show = ""                     lazy = false                 end                 if TemplatePar.framing():preprocess( sniffer ) == "" then                     if s == "1" then                         show = "*"                     else                         show = s                     end                     learn = true                 end             end         else             opt = { }         end         if lazy then             if not opt.cat then                 r = string.format( "%s %s",                                    submit,  factory( "noMSGnoCAT" ) )             end         else             r = submit         end         if r  and  not lazy then             local i             if not show  or  show == "*" then                 local e = mw.html.create( "span" )                                  :attr( "class", "error" )                                  :wikitext( "@@@" )                 if learn then                     local max  = 1000000000                     local id   = math.floor( os.clock() * max )                     local sign = string.format( "error_%d", id )                     local btn  = mw.html.create( "span" )                     local top  = mw.html.create( "div" )                     e:attr( "id", sign )                     btn:css( { ["background"]      = "#FFFF00",                                ["border"]          = "#FF0000 3px solid",                                ["font-weight"]     = "bold",                                ["padding"]         = "2px",                                ["text-decoration"] = "none" } )                        :wikitext( ">>>" )                     sign = string.format( "[[#%s|%s]]",                                           sign,  tostring( btn ) )                     top:wikitext( sign, " ", submit )                     mw.addWarning( tostring( top ) )                 end                 show = tostring( e )             end             i = show:find( "@@@", 1, true )             if i then                 -- No gsub() since r might contain "%3" (e.g. URL)                 r = string.format( "%s%s%s",                                    show:sub( 1,  i - 1 ),                                    r,                                    show:sub( i + 3 ) )             else                 r = show             end         end         if learn and r then             -- r = fatal( r )         end         s = opt.cat         if type( s ) == "string" then             local link             if opt.errNS then                 local ns = mw.title.getCurrentTitle().namespace                 local st = type( opt.errNS )                 if st == "string" then                     local space  = string.format( ".*%%s%d%%s.*", ns )                     local spaces = string.format( " %s ", opt.errNS )                     if spaces:match( space ) then                         link = true                     end                 elseif st == "table" then                     for i = 1, #opt.errNS do                         if opt.errNS[ i ] == ns then                             link = true                             break    -- for i                         end                     end -- for i                 end             else                 link = true             end             if 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()    local function finder( haystack, needle )     -- Find needle in haystack sequence     -- Precondition:     --     haystack  -- table; sequence of key names, downcased if low     --     needle    -- any; key name     -- Postcondition:     --     Return true iff found     local i     for i = 1, #haystack do         if haystack[ i ] == needle then             return true         end     end -- for i     return false end -- finder()    local function fix( valid, duty, got, options )     -- Perform parameter analysis     -- Precondition:     --     valid    -- table; unique sequence of known parameters     --     duty     -- table; sequence of mandatory parameters     --     got      -- table; sequence of current parameters     --     options  -- table or nil; optional details     -- Postcondition:     --     Return string as configured; empty if valid     -- Uses:     --     finder()     --     fault()     --     failure()     --     fed()     local r = false     local lack     for k, v in pairs( got ) do         if k == "" then             lack = true             break    -- for k, v         elseif not finder( valid, k ) then             r = fault( r, k )         end     end -- for k, v     if lack then         r = failure( "unavailable", false, options )     elseif r then         r = failure( "unknown",                      string.format( ""%s"", r ),                      options )     else -- all names valid         local i, s         for i = 1, #duty do             s = duty[ i ]             if not fed( got, s ) then                 r = fault( r, s )             end         end -- for i         if r then             r = failure( "undefined", r, options )         else -- all mandatory present             for i = 1, #duty do                 s = duty[ i ]                 if not got[ s ] then                     r = fault( r, s )                 end             end -- for i             if r then                 r = failure( "empty", r, options )             end         end     end     return r end -- fix()    local function flat( collection, options )     -- Return all table elements with downcased string     -- Precondition:     --     collection  -- table; k=v pairs     --     options     -- table or nil; optional messaging details     -- Postcondition:     --     Return table, may be empty; or string with error message.     -- Uses:     --     mw.ustring.lower()     --     fault()     --     failure()     local k, v     local r = { }     local e = false     for k, v in pairs( collection ) do         if type ( k ) == "string" then             k = mw.ustring.lower( k )             if r[ k ] then                 e = fault( e, k )             end         end         r[ k ] = v     end -- for k, v     if e then         r = failure( "multiSpell", e, options )     end     return r end -- flat()    local function fold( options )     -- Merge two tables, create new sequence if both not empty     -- Precondition:     --     options  -- table; details     --                 options.mandatory   sequence to keep unchanged     --                 options.optional    sequence to be appended     --                 options.low         downcased expected     -- Postcondition:     --     Return merged table, or message string if error     -- Uses:     --     finder()     --     fault()     --     failure()     --     flat()     local i, e, r, s     local base   = options.mandatory     local extend = options.optional     if #base == 0 then         if #extend == 0 then             r = { }         else             r = extend         end     else         if #extend == 0 then             r = base         else             e = false             for i = 1, #extend do                 s = extend[ i ]                 if finder( base, s ) then                     e = fault( e, s )                 end             end -- for i             if e then                 r = failure( "dupOpt", e, options )             else                 r = { }                 for i = 1, #base do                     table.insert( r, base[ i ] )                 end -- for i                 for i = 1, #extend do                     table.insert( r, extend[ i ] )                 end -- for i             end         end     end     if options.low  and  type( r ) == "table" then         r = flat( r, options )     end     return r end -- fold()    local function form( light, options, frame )     -- Run parameter analysis on current environment     -- Precondition:     --     light    -- true: template transclusion;  false: #invoke     --     options  -- table or nil; optional details     --                 options.mandatory     --                 options.optional     --     frame    -- object; #invoke environment, or false     -- Postcondition:     --     Return string with error message as configured;     --            false if valid     -- Uses:     --     TemplatePar.framing()     --     fold()     --     fetch()     --     fix()     --     finalize()     local duty, r     if frame then         TemplatePar.framing( frame )     end     if type( options ) == "table" then         if type( options.mandatory ) ~= "table" then             options.mandatory = { }         end         duty = options.mandatory         if type( options.optional ) ~= "table" then             options.optional = { }         end         r = fold( options )     else         options = { }         duty    = { }         r       = { }     end     if type( r ) == "table" then         local got = fetch( light, options )         if type( got ) == "table" then             r = fix( r, duty, got, options )         else             r = got         end     end     return finalize( r, options ) end -- form()    local function format( analyze, options )     -- Check validity of a value     -- Precondition:     --     analyze  -- string to be analyzed     --     options  -- table or nil; optional details     --                 options.say     --                 options.min     --                 options.max     -- Postcondition:     --     Return string with error message as configured;     --            false if valid or no answer permitted     -- Uses:     --     feasible()     --     failure()     local r = feasible( analyze, options, false )     local show     if options.min  and  not r then         if type( options.min ) == "number" then             if type( options.max ) == "number" then                 if options.max < options.min then                     r = failure( "minmax",                                  string.format( "%d > %d",                                                 options.min,                                                 options.max ),                                  options )                 end             end             if #analyze < options.min  and  not r then                 show = " <" .. options.min                 if options.say then                     show = string.format( "%s "%s"", show, options.say )                 end                 r = failure( "tooShort", show, options )             end         else             r = failure( "invalidPar", "min", options )         end     end     if options.max  and  not r then         if type( options.max ) == "number" then             if #analyze > options.max then                 show = " >" .. options.max                 if options.say then                     show = string.format( "%s "%s"", show, options.say )                 end                 r = failure( "tooLong", show, options )             end         else             r = failure( "invalidPar", "max", options )         end     end     return r end -- format()    local function formatted( assignment, access, options )     -- Check validity of one particular parameter in a collection     -- Precondition:     --     assignment  -- collection     --     access      -- id of parameter in collection     --     options     -- table or nil; optional details     -- Postcondition:     --     Return string with error message as configured;     --            false if valid or no answer permitted     -- Uses:     --     mw.text.trim()     --     format()     --     failure()     local r = false     if type( assignment ) == "table" then         local story = assignment.args[ access ] or ""         if type( access ) == "number" then             story = mw.text.trim( story )         end         if type( options ) ~= "table" then             options = { }         end         options.say = access         r = format( story, options )     end     return r end -- formatted()    local function furnish( frame, action )     -- Prepare #invoke evaluation of .assert() or .valid()     -- Precondition:     --     frame    -- object; #invoke environment     --     action   -- "assert" or "valid"     -- Postcondition:     --     Return string with error message or ""     -- Uses:     --     form()     --     failure()     --     finalize()     --     TemplatePar.valid()     --     TemplatePar.assert()     local options = { mandatory = { "1" },                       optional  = { "2",                                     "cat",                                     "errNS",                                     "low",                                     "max",                                     "min",                                     "format",                                     "preview",                                     "template" },                       template  = string.format( "#invoke:%s|%s|",                                                  "TemplatePar",                                                  action )                     }     local r       = form( false, options, frame )     if not r then         local s         options = { cat      = frame.args.cat,                     errNS    = frame.args.errNS,                     low      = frame.args.low,                     format   = frame.args.format,                     preview  = frame.args.preview,                     template = frame.args.template                   }         options = figure( frame.args[ 2 ], options )         if type( frame.args.min ) == "string" then             s = frame.args.min:match( "^%s*([0-9]+)%s*$" )             if s then                 options.min = tonumber( s )             else                 r = failure( "invalidPar",                              "min=" .. frame.args.min,                              options )             end         end         if type( frame.args.max ) == "string" then             s = frame.args.max:match( "^%s*([1-9][0-9]*)%s*$" )             if s then                 options.max = tonumber( s )             else                 r = failure( "invalidPar",                              "max=" .. frame.args.max,                              options )             end         end         if r then             r = finalize( r, options )         else             s = frame.args[ 1 ] or ""             r = tonumber( s )             if ( r ) then                 s = r             end             if action == "valid" then                 r = TemplatePar.valid( s, options )             elseif action == "assert" then                 r = TemplatePar.assert( s, "", options )             end         end     end     return r or "" end -- furnish()    TemplatePar.assert = function ( analyze, append, options )     -- Perform parameter analysis on a single string     -- Precondition:     --     analyze  -- string to be analyzed     --     append   -- string: append error message, prepending 
-- false or nil: throw error with message -- options -- table; optional details -- Postcondition: -- Return string with error message as configured; -- false if valid -- Uses: -- format() local r = format( analyze, options ) if ( r ) then if ( type( append ) == "string" ) then if ( append ~= "" ) then r = string.format( "%s
%s"
, append, r ) end else error( r, 0 ) end end return r end -- TemplatePar.assert() TemplatePar.check = function ( options ) -- Run parameter analysis on current template environment -- Precondition: -- options -- table or nil; optional details -- options.mandatory -- options.optional -- Postcondition: -- Return string with error message as configured; -- false if valid -- Uses: -- form() return form( true, options, false ) end -- TemplatePar.check() TemplatePar.count = function () -- Return number of template parameters -- Postcondition: -- Return number, starting at 0 -- Uses: -- mw.getCurrentFrame() -- frame:getParent() local k, v local r = 0 local t = mw.getCurrentFrame():getParent() local o = t.args for k, v in pairs( o ) do r = r + 1 end -- for k, v return r end -- TemplatePar.count() TemplatePar.countNotEmpty = function () -- Return number of template parameters with more than whitespace -- Postcondition: -- Return number, starting at 0 -- Uses: -- mw.getCurrentFrame() -- frame:getParent() local k, v local r = 0 local t = mw.getCurrentFrame():getParent() local o = t.args for k, v in pairs( o ) do if not v:match( "^%s*$" ) then r = r + 1 end end -- for k, v return r end -- TemplatePar.countNotEmpty() TemplatePar.downcase = function ( options ) -- Return all template parameters with downcased name -- Precondition: -- options -- table or nil; optional messaging details -- Postcondition: -- Return table, may be empty; or string with error message. -- Uses: -- mw.getCurrentFrame() -- frame:getParent() -- flat() local t = mw.getCurrentFrame():getParent() return flat( t.args, options ) end -- TemplatePar.downcase() TemplatePar.valid = function ( access, options ) -- Check validity of one particular template parameter -- Precondition: -- access -- id of parameter in template transclusion -- string or number -- options -- table or nil; optional details -- Postcondition: -- Return string with error message as configured; -- false if valid or no answer permitted -- Uses: -- mw.text.trim() -- TemplatePar.downcase() -- TemplatePar.framing() -- frame:getParent() -- formatted() -- failure() -- finalize() local r = type( access ) if r == "string" then r = mw.text.trim( access ) if #r == 0 then r = false end elseif r == "number" then r = access else r = false end if r then local params if type( options ) ~= "table" then options = { } end if options.low then params = TemplatePar.downcase( options ) else params = TemplatePar.framing():getParent() end r = formatted( params, access, options ) else r = failure( "noname", false, options ) end return finalize( r, options ) end -- TemplatePar.valid() TemplatePar.verify = function ( options ) -- Perform #invoke parameter analysis -- Precondition: -- options -- table or nil; optional details -- Postcondition: -- Return string with error message as configured; -- false if valid -- Uses: -- form() return form( false, options, false ) end -- TemplatePar.verify() TemplatePar.framing = function( frame ) -- Ensure availability of frame object -- Precondition: -- frame -- object; #invoke environment, or false -- Postcondition: -- Return frame object -- Uses: -- >< Local.frame if not Local.frame then if type( frame ) == "table" and type( frame.args ) == "table" and type( frame.getParent ) == "function" and type( frame:getParent() ) == "table" and type( frame:getParent().getParent ) == "function" and type( frame:getParent():getParent() ) == "nil" then Local.frame = frame else Local.frame = mw.getCurrentFrame() end end return Local.frame end -- TemplatePar.framing() Failsafe.failsafe = function ( atleast ) -- Retrieve versioning and check for compliance -- Precondition: -- atleast -- string, with required version -- or wikidata|item|~|@ or false -- Postcondition: -- Returns string -- with queried version/item, also if problem -- false -- if appropriate -- 2020-08-17 local since = atleast local last = ( since == "~" ) local linked = ( since == "@" ) local link = ( since == "item" ) local r if last or link or linked or since == "wikidata" then local item = Failsafe.item since = false if type( item ) == "number" and item > 0 then local suited = string.format( "Q%d", item ) if link then r = suited else local entity = mw.wikibase.getEntity( suited ) if type( entity ) == "table" then local seek = Failsafe.serialProperty or "P348" local vsn = entity:formatPropertyValues( seek ) if type( vsn ) == "table" and type( vsn.value ) == "string" and vsn.value ~= "" then if last and vsn.value == Failsafe.serial then r = false elseif linked then if mw.title.getCurrentTitle().prefixedText == mw.wikibase.getSitelink( suited ) then r = false else r = suited end else r = vsn.value end end end end end end if type( r ) == "nil" then if not since or since <= Failsafe.serial then r = Failsafe.serial else r = false end end return r end -- Failsafe.failsafe() -- Provide external access local p = {} function p.assert( frame ) -- Perform parameter analysis on some single string -- Precondition: -- frame -- object; #invoke environment -- Postcondition: -- Return string with error message or "" -- Uses: -- furnish() return furnish( frame, "assert" ) end -- p.assert() function p.check( frame ) -- Check validity of template parameters -- Precondition: -- frame -- object; #invoke environment -- Postcondition: -- Return string with error message or "" -- Uses: -- form() -- fill() local options = { optional = { "all", "opt", "cat", "errNS", "low", "format", "preview", "template" }, template = "#invoke:TemplatePar|check|" } local r = form( false, options, frame ) if not r then options = { mandatory = fill( frame.args.all ), optional = fill( frame.args.opt ), cat = frame.args.cat, errNS = frame.args.errNS, low = frame.args.low, format = frame.args.format, preview = frame.args.preview, template = frame.args.template } r = form( true, options, frame ) end return r or "" end -- p.check() function p.count( frame ) -- Count number of template parameters -- Postcondition: -- Return string with digits including "0" -- Uses: -- TemplatePar.count() return tostring( TemplatePar.count() ) end -- p.count() function p.countNotEmpty( frame ) -- Count number of template parameters which are not empty -- Postcondition: -- Return string with digits including "0" -- Uses: -- TemplatePar.countNotEmpty() return tostring( TemplatePar.countNotEmpty() ) end -- p.countNotEmpty() function p.match( frame ) -- Combined analysis of parameters and their values -- Precondition: -- frame -- object; #invoke environment -- Postcondition: -- Return string with error message or "" -- Uses: -- TemplatePar.framing() -- mw.text.trim() -- mw.ustring.lower() -- failure() -- form() -- TemplatePar.downcase() -- figure() -- feasible() -- fault() -- finalize() local r = false local options = { cat = frame.args.cat, errNS = frame.args.errNS, low = frame.args.low, format = frame.args.format, preview = frame.args.preview, template = frame.args.template } local k, v, s local params = { } TemplatePar.framing( frame ) for k, v in pairs( frame.args ) do if type( k ) == "number" then s, v = v:match( "^ *([^=]+) *= *(%S.*%S*) *$" ) if s then s = mw.text.trim( s ) if s == "" then s = false end end if s then if options.low then s = mw.ustring.lower( s ) end if params[ s ] then s = params[ s ] s[ #s + 1 ] = v else params[ s ] = { v } end else r = failure( "invalidPar", tostring( k ), options ) break -- for k, v end end end -- for k, v if not r then s = { } for k, v in pairs( params ) do s[ #s + 1 ] = k end -- for k, v options.optional = s r = form( true, options, frame ) end if not r then local errMiss, errValues, lack, rule local targs = frame:getParent().args options.optional = nil if options.low then targs = TemplatePar.downcase() else targs = frame:getParent().args end errMiss = false errValues = false for k, v in pairs( params ) do options.say = k s = targs[ k ] if s then if s == "" then lack = true else lack = false end else s = "" lack = true end for r, rule in pairs( v ) do options = figure( rule, options ) r = feasible( s, options, true ) if r then if lack then if errMiss then s = "%s, "%s"" errMiss = string.format( s, errMiss, k ) else errMiss = string.format( ""%s"", k ) end elseif not errMiss then errValues = fault( errValues, r ) end break -- for r, rule end end -- for s, rule end -- for k, v r = ( errMiss or errValues ) if r then if errMiss then r = failure( "undefined", errMiss, options ) else r = failure( "invalid", errValues, options ) end r = finalize( r, options ) end end return r or "" end -- p.match() function p.valid( frame ) -- Check validity of one particular template parameter -- Precondition: -- frame -- object; #invoke environment -- Postcondition: -- Return string with error message or "" -- Uses: -- furnish() return furnish( frame, "valid" ) end -- p.valid() p.failsafe = function ( frame ) -- Versioning interface local s = type( frame ) local since if s == "table" then since = frame.args[ 1 ] elseif s == "string" then since = frame end if since then since = mw.text.trim( since ) if since == "" then since = false end end return Failsafe.failsafe( since ) or "" end -- p.failsafe function p.TemplatePar() -- Retrieve function access for modules -- Postcondition: -- Return table with functions return TemplatePar end -- p.TemplatePar() setmetatable( p, { __call = function ( func, ... ) setmetatable( p, nil ) return Failsafe end } ) return p

Tags:

🔥 Trending searches on Wiki Bahasa Banjar:

17 Uktubir19816 April9PéntétGubangAmirikaTimpakulBanyuBungurIsuzu Panther20141 April12 MaiDyrøy8 JanuariSingapuraMalaysiaPiza21 DisimbirMoss (kota)Limau kuitPablo NerudaDario FoAmirika UtaraDarma Kasuma30 Siptimbir30 AgustusSalasaJakétAndra and The BackbonePukiKiblatHomoseksualitasLabanan Makmur, Teluk Bayur, BerauGagat12 DisimbirNan SarunaiWadai1985EkonomiKapalMichael Jackson30 NupimbirTupaiBugis1936KalimutPadangin, Tanta, Tabalong2018NurwigiaLawangLindu3Muara Ninian, Juai, BalanganPancasilaGalam22 April1974PilipinaMuhammad Arsyad al-BanjariGuhaBuntutSanayanRamadanHirangUyahGamalan🡆 More