Dokumentation für das Modul TemplateData[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Dieses Modul wurde von der deutschsprachigen Wikipedia importiert und leicht modifiziert. Es dient der erweiterten Darstellung der TemplateData zur Nutzung im VisualEditor.

Eine erweitere Dokumentation ist auf der deutschsprachigen Wikipedia verfügbar.

Versionsbezeichnung auf Wikidata: keine Version verfügbar.

Das Modul wird von der Vorlage {{TemplateData}} aufgerufen.

Benötigte weitere Module

Dieses Modul benötigt folgende weitere Module: Arguments • JSONutil • Multilingual • Namespace detect • TemplateData/config • Text • WLink • Yesno}}
Hinweise
local TemplateData = { suite  = "TemplateData",                        serial = "2021-07-05",                        item   = 46997995 } --[==[ improve template:TemplateData ]==] local Failsafe = TemplateData    local Config = {     -- multiple option names mapped into unique internal fields     basicCnf = { catProblem    = "strange",                  classNoNumTOC = "suppressTOCnum",                  classTable    = "classTable",                  cssParWrap    = "cssTabWrap",                  cssParams     = "cssTable",                  docpageCreate = "suffix",                  docpageDetect = "subpage",                  helpBoolean   = "support4boolean",                  helpContent   = "support4content",                  helpDate      = "support4date",                  helpFile      = "support4wiki-file-name",                  helpFormat    = "supportFormat",                  helpLine      = "support4line",                  helpNumber    = "support4number",                  helpPage      = "support4wiki-page-name",                  helpString    = "support4string",                  helpTemplate  = "support4wiki-template-name",                  helpURL       = "support4url",                  helpUser      = "support4wiki-user-name",                  msgDescMiss   = "solo",                  tStylesTOCnum = "stylesTOCnum" },     classTable     = { "wikitable" },    -- classes for params table     debugmultilang = "C0C0C0",     loudly         = false,    -- show exported element, etc.     solo           = false,    -- complaint on missing description     strange        = false,    -- title of maintenance category     cssTable       = false,    -- styles for params table     cssTabWrap     = false,    -- styles for params table wrapper     debug          = false,     subpage        = false,    -- pattern to identify subpage     suffix         = false,    -- subpage creation scheme     suppressTOCnum = false,    -- class for TOC number suppression     jsonDebug      = "json-code-lint"    -- class for jsonDebug tool } local Data = {     div     = false,    -- <div class="mw-templatedata-doc-wrap">     got     = false,    -- table, initial templatedata object     heirs   = false,    -- table, params that are inherited     jump    = false,    -- source position at end of "params"     less    = false,    -- main description missing     lasting = false,    -- old syntax encountered     lazy    = false,    -- doc mode; do not generate effective <templatedata>     leading = false,    -- show TOC --  low     = false,    -- 1= mode     order   = false,    -- parameter sequence     params  = false,    -- table, exported parameters     scream  = false,    -- error messages     sibling = false,    -- TOC juxtaposed     slang   = nil,      -- project/user language code     slim    = false,    -- JSON reduced to plain     source  = false,    -- JSON input     strip   = false,    -- <templatedata> evaluation     tag     = false,    -- table, exported root element     title   = false,    -- page     tree    = false     -- table, rewritten templatedata object } local Permit = {     builder = { after           = "block",                 align           = "block",                 block           = "block",                 compressed      = "block",                 dense           = "block",                 grouped         = "inline",                 half            = "inline",                 indent          = "block",                 inline          = "inline",                 last            = "block",                 lead            = "block",                 newlines        = "*",                 spaced          = "inline" },     colors  = { tableheadbg = "B3B7FF",                 required    = "EAF3FF",                 suggested   = "FFFFFF",                 optional    = "EAECF0",                 deprecated  = "FFCBCB" },     params  = { aliases         = "table",                 autovalue       = "string",                 default         = "string table I18N nowiki",                 deprecated      = "boolean string I18N",                 description     = "string table I18N",                 example         = "string table I18N nowiki",                 label           = "string table I18N",                 inherits        = "string",                 required        = "boolean",                 style           = "string table",                 suggested       = "boolean",                 suggestedvalues = "string table number",                 type            = "string" },     root    = { description = "string table I18N",                 format      = "string",                 maps        = "table",                 params      = "table",                 paramOrder  = "table",                 sets        = "table" },     search  = "[{,]%%s*(['\"])%s%%1%%s*:%%s*%%{",     types   = { boolean                   = true,                 content                   = true,                 date                      = true,                 line                      = true,                 number                    = true,                 string                    = true,                 unknown                   = true,                 url                       = true,                 ["wiki-file-name"]        = true,                 ["wiki-page-name"]        = true,                 ["wiki-template-name"]    = true,                 ["wiki-user-name"]        = true,                 ["unbalanced-wikitext"]   = true,                 ["string/line"]           = "line",                 ["string/wiki-page-name"] = "wiki-page-name",                 ["string/wiki-user-name"] = "wiki-user-name" } }    local function Fault( alert )     -- Memorize error message     -- Parameter:     --     alert  -- string, error message     if Data.scream then         Data.scream = string.format( "%s *** %s", Data.scream, alert )     else         Data.scream = alert     end end -- Fault()    local function Fetch( ask, allow )     -- Fetch module     -- Parameter:     --     ask    -- string, with name     --                       "/global"     --                       "JSONutil"     --                       "Multilingual"     --                       "Text"     --                       "WLink"     --     allow  -- true: no error if unavailable     -- Returns table of module     -- error: Module not available     local sign = ask     local r, stem     if sign:sub( 1, 1 ) == "/" then         sign = TemplateData.frame:getTitle() .. sign     else         stem = sign         sign = "Module:" .. stem     end     if TemplateData.extern then         r = TemplateData.extern[ sign ]     else         TemplateData.extern = { }     end     if not r then         local lucky, g = pcall( require, sign )         if type( g ) == "table" then             if stem  and  type( g[ stem ] ) == "function" then                 r = g[ stem ]()             else                 r = g             end             TemplateData.extern[ sign ] = r         elseif not allow then             error( string.format( "Fetch(%s) %s", sign, g ), 0 )         end     end     return r end -- Fetch()    local function Foreign()     -- Guess human language     -- Returns slang, or not     if type( Data.slang ) == "nil" then         local Multilingual = Fetch( "Multilingual", true )         if Multilingual  and            type( Multilingual.userLangCode ) == "function" then             Data.slang = Multilingual.userLangCode()         else             Data.slang = mw.language.getContentLanguage():getCode()                                                          :lower()         end     end     if Data.slang  and        mw.ustring.codepoint( Data.slang, 1, 1 ) > 122 then         Data.slang = false     end     return Data.slang end -- Foreign()    local function facet( ask, at )     -- Find physical position of parameter definition in JSON     -- Parameter:     --     ask  -- string, parameter name     --     at   -- number, physical position within definition     -- Returns number, or nil     local seek = string.format( Permit.search,                                 ask:gsub( "%%", "%%%%" )                                    :gsub( "([%-.()+*?^$%[%]])",                                           "%%%1" ) )     local i, k, r, slice, source     if not Data.jump then         Data.jump = Data.source:find( "params", 2 )         if Data.jump then             Data.jump = Data.jump + 7         else             Data.jump = 1         end     end     i, k = Data.source:find( seek,  at + Data.jump )     while i  and  not r do         source = Data.source:sub( k + 1 )         slice  = source:match( "^%s*\"([^\"]+)\"s*:" )         if not slice then             slice = source:match( "^%s*'([^']+)'%s*:" )         end         if ( slice and Permit.params[ slice ] )   or            source:match( "^%s*%}" ) then             r = k         else             i, k = Data.source:find( seek,  k )         end     end    -- while i     return r end -- facet()    local function factory( adapt )     -- Retrieve localized text from system message     -- Parameter:     --     adapt  -- string, message ID after "templatedata-"     -- Returns string, with localized text     local o = mw.message.new( "templatedata-" .. adapt )     if Foreign() then         o:inLanguage( Data.slang )     end     return o:plain() end -- factory()    local function faculty( adjust )     -- Test template arg for boolean     --     adjust  -- string or nil     -- Returns boolean     local s = type( adjust )     local r     if s == "string" then         r = mw.text.trim( adjust )         r = ( r ~= ""  and  r ~= "0" )     elseif s == "boolean" then         r = adjust     else         r = false     end     return r end -- faculty()    local function failures()     -- Retrieve error collection and category     -- Returns string     local r     if Data.scream then         local e = mw.html.create( "span" )                          :addClass( "error" )                          :wikitext( Data.scream )         r = tostring( e )         mw.addWarning( "'''TemplateData'''<br />" .. Data.scream )         if Config.strange then             r = string.format( "%s[[category:%s]]",                                r,                                Config.strange )         end     else         r = ""     end     return r end -- failures()    local function fair( adjust )     -- Reduce text to one line of plain text, or noexport wikitext blocks     --     adjust  -- string     -- Returns string, with adjusted text     local f    = function ( a )                      return a:gsub( "%s*\n%s*", " " )                              :gsub( "%s%s+", " " )                  end     local tags = { { start = "<noexport>",                      stop  = "</noexport>" },                    { start = "<exportonly>",                      stop  = "</exportonly>",                      l     = false }                  }     local r = adjust     local i, j, k, s, tag     for m = 1, 2 do         tag = tags[ m ]         if r:find( tag.start, 1, true ) then             s     = r             r     = ""             i     = 1             tag.l = true             j, k  = s:find( tag.start, i, true )             while j do                 if j > 1 then                     r = r .. f( s:sub( i,  j - 1 ) )                 end                 i    = k + 1                 j, k = s:find( tag.stop, i, true )                 if j then                     if m == 1 then                         r = r .. s:sub( i,  j - 1 )                     end                     i    = k + 1                     j, k = s:find( tag.start, i, true )                 else                     Fault( "missing " .. tag.stop )                 end             end    -- while j             r = r .. s:sub( i )         elseif m == 1 then             r = f( r )         end     end -- for m     if tags[ 2 ].l then         r = r:gsub( "<exportonly>.*</exportonly>", "" )     end     return r end -- fair()    local function fancy( advance, alert )     -- Present JSON source     -- Parameter:     --     advance  -- true, for nice     --     alert    -- true, for visible     -- Returns string     local r     if Data.source then         local support = Config.jsonDebug         local css         if advance then             css = { height = "6em",                     resize = "vertical" }             r   = { [ 1 ] = "syntaxhighlight",                     [ 2 ] = Data.source,                     lang  = "json",                     style = table.concat( css, ";" ) }             if alert then                 r.class( support )             end             r = TemplateData.frame:callParserFunction( "#tag", r )         else             css = { [ "font-size" ]   = "77%",                     [ "line-height" ] = "1.35" }             if alert then                 css.resize = "vertical"             else                 css.display = "none"             end             r = mw.html.create( "pre" )                        :addClass( support )                        :css( css )                        :wikitext( mw.text.encode( Data.source ) )             r = tostring( r )         end         r = "\n".. r     else         r = ""     end     return r end -- fancy()    local function faraway( alternatives )     -- Retrieve best language version from multilingual text     -- Parameter:     --     alternatives  -- table, to be evaluated     -- Returns     --     1  -- string, with best match     --     2  -- table of other versions, if any     local n = 0     local variants = { }     local r1, r2     for k, v in pairs( alternatives ) do         if type( v ) == "string" then             v = mw.text.trim( v )             if v ~= ""  and  type( k ) == "string" then                 k = k:lower()                 variants[ k ] = v                 n             = n + 1             end         end     end -- for k, v     if n > 0 then         local Multilingual = Fetch( "Multilingual", true )         if Multilingual  and            type( Multilingual.i18n ) == "function" then             local show, slang = Multilingual.i18n( variants )             if show then                 r1 = show                 variants[ slang ] = nil                 r2 = variants             end         end         if not r1 then             Foreign()             for k, v in pairs( variants ) do                 if n == 1 then                     r1 = v                 elseif Data.slang == k then                     variants[ k ] = nil                     r1 = v                     r2 = variants                 end             end -- for k, v         end         if r2 and Multilingual then             for k, v in pairs( r2 ) do                 if v  and  not Multilingual.isLang( k, true ) then                     Fault( string.format( "%s <code>lang=%s</code>",                                           "Invalid",                                           k ) )                 end             end -- for k, v         end     end     return r1, r2 end -- faraway()    local function fashioned( about, asked, assign )     -- Create description head     -- Parameter:     --     about   -- table, supposed to contain description     --     asked   -- true, if mandatory description     --     assign  -- <block>, if to be equipped     -- Returns <block>, with head, or nil     local para = assign or mw.html.create( "div" )     local plus, r     if about and about.description then         if type( about.description ) == "string" then             para:wikitext( about.description )         else             para:wikitext( about.description[ 1 ] )             plus = mw.html.create( "ul" )             plus:css( "text-align", "left" )             for k, v in pairs( about.description[ 2 ] ) do                 plus:node( mw.html.create( "li" )                                   :node( mw.html.create( "code" )                                                 :wikitext( k ) )                                   :node( mw.html.create( "br" ) )                                   :wikitext( fair( v ) ) )             end -- for k, v             if Config.loudly then                 plus = mw.html.create( "div" )                               :css( "background-color",                                     "#" .. Config.debugmultilang )                               :node( plus )             else                 plus:addClass( "templatedata-maintain" )                     :css( "display", "none" )             end         end     elseif Config.solo and asked then         para:addClass( "error" )             :wikitext( Config.solo )         Data.less = true     else         para = false     end     if para then         if plus then             r = mw.html.create( "div" )                        :node( para )                        :node( plus )         else             r = para         end     end     return r end -- fashioned()    local function fatten( access )     -- Create table row for sub-headline     -- Parameter:     --     access  -- string, with name     -- Returns <tr>     local param     = Data.tree.params[ access ]     local sub, sort = access:match( "(=+)%s*(%S.*)$" )     local headline  = mw.html.create( string.format( "h%d", #sub ) )     local r         = mw.html.create( "tr" )     local td        = mw.html.create( "td" )                              :attr( "colspan", "5" )                              :attr( "data-sort-value",  "!" .. sort )     local s     if param.style then         s = type( param.style )         if s == "table" then             td:css( param.style )         elseif s == "string" then             td:cssText( param.style )         end     end     s = fashioned( param, false, headline )     if s then         headline = s     else         headline:wikitext( sort )     end     td:node( headline )     r:node( td )     return r end -- fatten()    local function fathers()     -- Merge params with inherited values     local n = 0     local p = Data.params     local t = Data.tree.params     local p2, t2     for k, v in pairs( Data.heirs ) do         n = n + 1     end -- for k, v     for i = 1, n do         if Data.heirs then             for k, v in pairs( Data.heirs ) do                 if v  and  not Data.heirs[ v ] then                     n               = n - 1                     t[ k ].inherits = nil                     Data.heirs[ k ] = nil                     p2              = { }                     t2              = { }                     if p[ v ] then                         for k2, v2 in pairs( p[ v ] ) do                             p2[ k2 ] = v2                         end -- for k2, v2                         if p[ k ] then                             for k2, v2 in pairs( p[ k ] ) do                                 if type( v2 ) ~= "nil" then                                     p2[ k2 ] = v2                                 end                             end -- for k2, v2                         end                         p[ k ] = p2                         for k2, v2 in pairs( t[ v ] ) do                             t2[ k2 ] = v2                         end -- for k2, v2                         for k2, v2 in pairs( t[ k ] ) do                             if type( v2 ) ~= "nil" then                                 t2[ k2 ] = v2                             end                         end -- for k2, v2                         t[ k ] = t2                     else                         Fault( "No params[] inherits " .. v )                     end                 end             end -- for k, v         end     end -- i = 1, n     if n > 0 then         local s         for k, v in pairs( Data.heirs ) do             if v then                 if s then                     s = string.format( "%s &#124; %s", s, k )                 else                     s = "Circular inherits: " .. k                 end             end         end -- for k, v         Fault( s )     end end -- fathers()    local function favorize()     -- Local customization issues     local boole  = { ["font-size"] = "125%" }     local l, cx = pcall( mw.loadData,                          TemplateData.frame:getTitle() .. "/config" )     local scripting     TemplateData.ltr = not mw.language.getContentLanguage():isRTL()     if TemplateData.ltr then         scripting = "left"     else         scripting = "right"     end     boole[ "margin-" .. scripting ] = "3em"     Permit.boole = { [false] = { css  = boole,                                  lead = true,                                  show = "&#x2610;" },                      [true]  = { css  = boole,                                  lead = true,                                  show = "&#x2611;" } }     Permit.css   = { }     for k, v in pairs( Permit.colors ) do         if k == "tableheadbg" then             k = "tablehead"         end         Permit.css[ k ] = { ["background-color"]  =  "#" .. v }     end -- for k, v     if type( cx ) == "table" then         local c, s         if type( cx.permit ) == "table" then             if type( cx.permit.boole ) == "table" then                 if type( cx.permit.boole[ true ] ) == "table" then                     Permit.boole[ false ]  = cx.permit.boole[ false ]                 end                 if type( cx.permit.boole[ true ] ) == "table" then                     Permit.boole[ true ]  = cx.permit.boole[ true ]                 end             end             if type( cx.permit.css ) == "table" then                 for k, v in pairs( cx.permit.css ) do                     if type( v ) == "table" then                         Permit.css[ k ] = v                     end                 end -- for k, v             end         end         for k, v in pairs( Config.basicCnf ) do             s = type( cx[ k ] )             if s == "string"  or  s == "table" then                 Config[ v ] = cx[ k ]             end         end -- for k, v     end     if type( Config.subpage ) ~= "string"  or        type( Config.suffix ) ~= "string" then         local got = mw.message.new( "templatedata-doc-subpage" )         local suffix         if got:isDisabled() then             suffix = "doc"         else             suffix = got:plain()         end         if type( Config.subpage ) ~= "string" then             Config.subpage = string.format( "/%s$", suffix )         end         if type( Config.suffix ) ~= "string" then             Config.suffix = string.format( "%%s/%s", suffix )         end     end end -- favorize()    local function feasible( about, at )     -- Deal with suggestedvalues within parameter     -- Parameter:     --     about  -- parameter details     --               .suggestedvalues  -- table|string|number,     --                                    value and possibly description     --                                    .code   -- mandatory     --                                    .label  -- table|string     --                                    .icon   -- string     --                                    .class  -- table|string     --                                    .css    -- table     --                                    .style  -- string     --                                    .less   -- true: suppress code     --               .type     --     at     -- string, with parameter name     -- Returns     --     1: mw.html object     --     2: sequence table with values, or nil     local p = about.suggestedvalues     local s = type( p )     local e, r1, r2, v     if s == "table" then         if #p > 0 then             for i = 1, #p do                 e = p[ i ]                 s = type( e )                 if s == "table" then                     if type( e.code ) == "string" then                         s = mw.text.trim( e.code )                         if s == "" then                             e = nil                         else                             e.code = s                         end                     else                         e = nil                         s = string.format( "params.%s.%s[%d] %s",                                            at,                                            "suggestedvalues",                                            i,                                            "MISSING 'code:'" )                     end                 elseif s == "string" then                     s = mw.text.trim( e )                     if s == "" then                         e = nil                         s = string.format( "params.%s.%s[%d] EMPTY",                                            at, "suggestedvalues", i )                         Fault( s )                     else                         e = { code = s }                     end                 elseif s == "number" then                     e = { code = tostring( e ) }                 else                     s = string.format( "params.%s.%s[%d] INVALID",                                        at, "suggestedvalues", i )                     Fault( s )                     e = false                 end                 if e then                     v = v  or  { }                     table.insert( v, e )                 end             end -- for i         else             Fault( string.format( "params.%s.suggestedvalues %s",                    at, "NOT AN ARRAY" ) )         end     elseif s == "string" then         s = mw.text.trim( p )         if s ~= "" then             v = { }             table.insert( v,                           { code = s } )         end     elseif s == "number" then         v = { }         table.insert( v,                       { code = tostring( p ) } )     end     if v then         local d, less, story, swift, t, u         r1 = mw.html.create( "ul" )         r2 = { }         for i = 1, #v do             u = mw.html.create( "li" )             e = v[ i ]             table.insert( r2, e.code )             story = false             less  = ( e.less == true )             if not less then                 swift = e.code                 if e.support then                     local scream, support                     s = type( e.support )                     if s == "string" then                         support = e.support                     elseif s == "table" then                         support = faraway( e.support )                     else                         scream = "INVALID"                     end                     if support then                         s = mw.text.trim( support )                         if s == "" then                             scream = "EMPTY"                         elseif s:find( "[%[%]|%<%>]" ) then                             scream = "BAD PAGE"                         else                             support = s                         end                     end                     if scream then                         s = string.format( "params.%s.%s[%d].support %s",                                            at,                                            "suggestedvalues",                                            i,                                            scream )                         Fault( s )                     else                         swift = string.format( "[[:%s|%s]]",                                                support, swift )                     end                 end                 if about.type:sub( 1, 5 ) == "wiki-"  and                    swift == e.code then                     local rooms = { file = 6,                                     temp = 10,                                     user = 2 }                     local ns = rooms[ about.type:sub( 6, 9 ) ]  or  0                     t = mw.title.makeTitle( ns, swift )                     if t and t.exists then                         swift = string.format( "[[:%s|%s]]",                                                t.prefixedText, swift )                     end                 end                 u:node( mw.html.create( "code" )                                :css( "white-space", "nowrap" )                                :wikitext( swift ) )             end             if e.class then                 s = type( e.class )                 if s == "string" then                     u:addClass( e.class )                 elseif s == "table" then                     for k, s in pairs( e.class ) do                         u:addClass( s )                     end -- for k, s                 else                     s = string.format( "params.%s.%s[%d].class INVALID",                                        at, "suggestedvalues", i )                     Fault( s )                 end             end             if e.css then                 if type( e.css ) == "table" then                     u:css( e.css )                 else                     s = string.format( "params.%s.%s[%d].css INVALID",                                        at, "suggestedvalues", i )                     Fault( s )                 end             end             if e.style then                 if type( e.style ) == "string" then                     u:cssText( e.style )                 else                     s = string.format( "params.%s.%s[%d].style INVALID",                                        at, "suggestedvalues", i )                     Fault( s )                 end             end             if about.type == "wiki-file-name"  and  not e.icon then                 e.icon = e.code             end             if e.label then                 s = type( e.label )                 if s == "string" then                     s = mw.text.trim( e.label )                     if s == "" then                         s = string.format( "params.%s.%s[%d].label %s",                                            at,                                            "suggestedvalues",                                            i,                                            "EMPTY" )                         Fault( s )                     else                         story = s                     end                 elseif s == "table" then                     story = faraway( e.label )                 else                     s = string.format( "params.%s.%s[%d].label INVALID",                                        at, "suggestedvalues", i )                     Fault( s )                 end             end             s = false             if type( e.icon ) == "string" then                 t = mw.title.makeTitle( 6, e.icon )                 if t and t.file.exists then                     local g = mw.html.create( "span" )                     s = string.format( "[[%s|16px]]", t.prefixedText )                     g:attr( "role", "presentation" )                      :wikitext( s )                     s = tostring( g )                 end             end             if not s  and  not less  and  e.label then                 s = mw.ustring.char( 0x2013 )             end             if s then                 d = mw.html.create( "span" )                            :wikitext( s )                 if TemplateData.ltr then                     if not less then                         d:css( "margin-left", "0.5em" )                     end                     if story then                         d:css( "margin-right", "0.5em" )                     end                 else                     if not less then                         d:css( "margin-right", "0.5em" )                     end                     if story then                         d:css( "margin-left", "0.5em" )                     end                 end                 u:node( d )             end             if story then                 u:wikitext( story )             end             r1:newline()               :node( u )         end -- for i     end     if not r1 then         Fault( string.format( "params.%s.suggestedvalues INVALID", at ) )         r1 = mw.html.create( "code" )                     :addClass( "error" )                     :wikitext( "INVALID" )     end     return r1, r2 end -- feasible()    local function feat()     -- Check and store parameter sequence     if Data.source then         local i = 0         local s         for k, v in pairs( Data.tree.params ) do             if i == 0 then                 Data.order = { }                 i = 1                 s = k             else                 i = 2                 break -- for k, v             end         end -- for k, v         if i > 1 then             local pointers = { }             local points   = { }             local given    = { }             for k, v in pairs( Data.tree.params ) do                 i = facet( k, 1 )                 if type( v ) == "table" then                     if type( v.label ) == "string" then                         s = mw.text.trim( v.label )                         if s == "" then                             s = k                         end                     else                         s = k                     end                     if given[ s ] then                         if given[ s ] == 1 then                             local scream = "Parameter label '%s' detected multiple times"                             Fault( string.format( scream, s ) )                             given[ s ] = 2                         end                     else                         given[ s ] = 1                     end                 end                 if i then                     table.insert( points, i )                     pointers[ i ] = k                     i = facet( k, i )                     if i then                         s = "Parameter '%s' detected twice"                         Fault( string.format( s, k ) )                     end                 else                     s = "Parameter '%s' not detected"                     Fault( string.format( s, k ) )                 end             end -- for k, v             table.sort( points )             for i = 1, #points do                 table.insert( Data.order,  pointers[ points[ i ] ] )             end -- i = 1, #points         elseif s then             table.insert( Data.order, s )         end     end end -- feat()    local function feature( access )     -- Create table row for parameter, check and display violations     -- Parameter:     --     access  -- string, with name     -- Returns <tr>     local mode, s, status     local fine    = function ( a )                         s = mw.text.trim( a )                         return a == s  and                                a ~= ""  and                                not a:find( "%|=\n" )  and                                not a:find( "%s%s" )                     end     local begin   = mw.html.create( "td" )     local code    = mw.html.create( "code" )     local desc    = mw.html.create( "td" )     local eager   = mw.html.create( "td" )     local legal   = true     local param   = Data.tree.params[ access ]     local ranking = { "required", "suggested", "optional", "deprecated" }     local r       = mw.html.create( "tr" )     local styles  = "mw-templatedata-doc-param-"     local sort, typed      for k, v in pairs( param ) do         if v == "" then             param[ k ] = false         end     end -- for k, v      -- label     sort = param.label or access     if sort:match( "^%d+$" ) then         begin:attr( "data-sort-value",                     string.format( "%05d", tonumber( sort ) ) )     end     begin:css( "font-weight", "bold" )          :wikitext( sort )      -- name and aliases     code:css( "font-size", "92%" )         :css( "white-space", "nowrap" )         :wikitext( access )     if not fine( access ) then         code:addClass( "error" )         Fault( string.format( "Bad ID params.<code>%s</code>", access ) )         legal = false         begin:attr( "data-sort-value",  " " .. sort )     end     code = mw.html.create( "td" )                   :addClass( styles .. "name" )                   :node( code )     if access:match( "^%d+$" ) then         code:attr( "data-sort-value",                    string.format( "%05d", tonumber( access ) ) )     end     if type( param.aliases ) == "table" then         local lapsus, syn         for k, v in pairs( param.aliases ) do             code:tag( "br" )             if type( v ) == "string" then                 if not fine( v ) then                     lapsus = true                     code:node( mw.html.create( "span" )                                       :addClass( "error" )                                       :css( "font-style", "italic" )                                       :wikitext( "string" ) )                         :wikitext( s )                 else                     syn = mw.html.create( "span" )                                  :addClass( styles .. "alias" )                                  :css( "white-space", "nowrap" )                                  :wikitext( s )                     code:node( syn )                 end             else                 lapsus = true                 code:node( mw.html.create( "code" )                                   :addClass( "error" )                                   :wikitext( type( v ) ) )             end         end -- for k, v         if lapsus then             s = string.format( "params.<code>%s</code>.aliases", access )             Fault(  factory( "invalid-value" ):gsub( "$1", s )  )             legal = false         end     end      -- description etc.     s = fashioned( param )     if s then         desc:node( s )     end     if param.style then         s = type( param.style )         if s == "table" then             desc:css( param.style )         elseif s == "string" then             desc:cssText( param.style )         end     end     if param.suggestedvalues or        param.default or        param.example or        param.autovalue then         local details = { "suggestedvalues",                           "default",                           "example",                           "autovalue" }         local dl      = mw.html.create( "dl" )         local dd, section, show         for i = 1, #details do             s    = details[ i ]             show = param[ s ]             if show then                 dd      = mw.html.create( "dd" )                 section = factory( "doc-param-" .. s )                 if param.type == "boolean"   and                    ( show == "0" or show == "1" ) then                     local boole = Permit.boole[ ( show == "1" ) ]                     if boole.lead == true then                         dd:node( mw.html.create( "code" )                                         :wikitext( show ) )                           :wikitext( " " )                     end                     if type( boole.show ) == "string" then                         local v = mw.html.create( "span" )                                          :attr( "aria-hidden", "true" )                                          :wikitext( boole.show )                         if boole.css then                             v:css( boole.css )                         end                         dd:node( v )                     end                     if type( boole.suffix ) == "string" then                         dd:wikitext( boole.suffix )                     end                     if boole.lead == false then                         dd:wikitext( " " )                           :node( mw.html.create( "code" )                                         :wikitext( show ) )                     end                 elseif s == "suggestedvalues" then                     local html, values = feasible( param, access )                     dd:newline()                        :node( html )                     Data.params[ access ].suggestedvalues = values                 else                     dd:wikitext( show )                 end                 dl:node( mw.html.create( "dt" )                                 :wikitext( section ) )                   :node( dd )             end         end -- i = 1, #details         desc:node( dl )     end      -- type     if type( param.type ) == "string" then         param.type = mw.text.trim( param.type )         if param.type == "" then             param.type = false         end     end     if param.type then         s     = Permit.types[ param.type ]         typed = mw.html.create( "td" )                   :addClass( styles .. "type" )         if s then             if s == "string" then                 Data.params[ access ].type = s                 typed:wikitext( factory( "doc-param-type-" .. s ) )                      :tag( "br" )                 typed:node( mw.html.create( "span" )                                    :addClass( "error" )                                    :wikitext( param.type ) )                 Data.lasting = true             else                 local support = Config[ "support4" .. param.type ]                 s = factory( "doc-param-type-" .. param.type )                 if support then                     s = string.format( "[[%s|%s]]", support, s )                 end                 typed:wikitext( s )             end         else             Data.params[ access ].type = "unknown"             typed:addClass( "error" )                  :wikitext( "INVALID" )             s = string.format( "params.<code>%s</code>.type", access )             Fault(  factory( "invalid-value" ):gsub( "$1", s )  )             legal = false         end     else         typed = mw.html.create( "td" )                    :wikitext( factory( "doc-param-type-unknown" ) )         Data.params[ access ].type = "unknown"         if param.default then             Data.params[ access ].default = nil             Fault( "Default value requires <code>type</code>" )             legal = false         end     end     -- status     if param.required then         mode = 1         if param.autovalue then             Fault( string.format( "autovalued <code>%s</code> required",                                   access ) )             legal = false         end         if param.default then             Fault( string.format( "Defaulted <code>%s</code> required",                                   access ) )             legal = false         end         if param.deprecated then             Fault( string.format( "Required deprecated <code>%s</code>",                                   access ) )             legal = false         end     elseif param.deprecated then         mode = 4     elseif param.suggested then         mode = 2     else         mode = 3     end     status = ranking[ mode ]     ranking = factory( "doc-param-status-" .. status )     if mode == 1  or  mode == 4 then         ranking = mw.html.create( "span" )                          :css( "font-weight", "bold" )                          :wikitext( ranking )         if type( param.deprecated ) == "string" then             ranking:tag( "br" )             ranking:wikitext( param.deprecated )         end         if param.suggested  and  mode == 4 then             s = string.format( "Suggesting deprecated <code>%s</code>",                                access )             Fault( s )             legal = false         end     end     eager:attr( "data-sort-value", tostring( mode ) )                 :node( ranking )                 :addClass( string.format( "%sstatus-%s",                                           styles, status ) )      -- <tr>     r:attr( "id",  "templatedata:" .. mw.uri.anchorEncode( access ) )      :css( Permit.css[ status ] )      :addClass( styles .. status )      :node( begin )      :node( code )      :node( desc )      :node( typed )      :node( eager )      :newline()     if not legal then         r:css( "border", "#FF0000 3px solid" )     end     return r end -- feature()    local function features()     -- Create <table> for parameters     -- Returns <table>, or nil     local r     if Data.tree and Data.tree.params then         local tbl = mw.html.create( "table" )         local tr  = mw.html.create( "tr" )         feat()         if Data.order  and  #Data.order > 1 then             tbl:addClass( "sortable" )         end         if type( Config.classTable ) == "table" then             for k, v in pairs( Config.classTable ) do                 tbl:addClass( v )             end -- for k, v         end         if type( Config.cssTable ) == "table" then             tbl:css( Config.cssTable )         end         tr:node( mw.html.create( "th" )                         :attr( "colspan", "2" )                         :css( Permit.css.tablehead )                         :wikitext( factory( "doc-param-name" ) ) )           :node( mw.html.create( "th" )                         :css( Permit.css.tablehead )                         :wikitext( factory( "doc-param-desc" ) ) )           :node( mw.html.create( "th" )                         :css( Permit.css.tablehead )                         :wikitext( factory( "doc-param-type" ) ) )           :node( mw.html.create( "th" )                         :css( Permit.css.tablehead )                         :wikitext( factory( "doc-param-status" ) ) )         tbl:newline() --         :node( mw.html.create( "thead" )                          :node( tr ) --              )            :newline()         if Data.order then             local leave, s             for i = 1, #Data.order do                 s = Data.order[ i ]                 if s:sub( 1, 1 ) == "=" then                     leave = true                     tbl:node( fatten( s ) )                     Data.order[ i ] = false                 elseif s:match( "[=|]" ) then                     Fault( string.format( "Bad param <code>%s</code>",                                           s ) )                 else                     tbl:node( feature( s ) )                 end             end -- for i = 1, #Data.order             if leave then                 for i = #Data.order, 1, -1 do                     if not Data.order[ i ] then                         table.remove( Data.order, i )                     end                 end -- for i = #Data.order, 1, -1             end             Data.tag.paramOrder = Data.order         end         if Config.cssTabWrap or Data.scroll then             r = mw.html.create( "div" )             if type( Config.cssTabWrap ) == "table" then                 r:css( Config.cssTabWrap )             elseif type( Config.cssTabWrap ) == "string" then                 -- deprecated                 r:cssText( Config.cssTabWrap )             end             if Data.scroll then                 r:css( "height",   Data.scroll )                  :css( "overflow", "auto" )             end             r:node( tbl )         else             r = tbl         end     end     return r end -- features()    local function fellow( any, assigned, at )     -- Check sets[] parameter and issue error message, if necessary     -- Parameter:     --     any       -- should be number     --     assigned  -- parameter name     --     at        -- number, of set     local s     if type( any ) ~= "number" then         s = "<code>sets[%d].params[%s]</code>??"         Fault( string.format( s,                               at,                               mw.text.nowiki( tostring( any ) ) ) )     elseif type( assigned ) == "string" then         if not Data.got.params[ assigned ] then             s = "<code>sets[%d].params %s</code> is undefined"             Fault( string.format( s, at, assigned ) )         end     else         s = "<code>sets[%d].params[%d] = %s</code>??"         Fault( string.format( s,  k,  type( assigned ) ) )     end end -- fellow()    local function fellows()     -- Check sets[] and issue error message, if necessary     local s     if type( Data.got.sets ) == "table" then         if type( Data.got.params ) == "table" then             for k, v in pairs( Data.got.sets ) do                 if type( k ) == "number" then                     if type( v ) == "table" then                         for ek, ev in pairs( v ) do                             if ek == "label" then                                 s = type( ev )                                 if s ~= "string"  and                                    s ~= "table" then                                     s = "<code>sets[%d].label</code>??"                                     Fault( string.format( s, k ) )                                 end                             elseif ek == "params"  and                                 type( ev ) == "table" then                                 for pk, pv in pairs( ev ) do                                     fellow( pk, pv, k )                                 end -- for pk, pv                             else                                 ek = mw.text.nowiki( tostring( ek ) )                                 s  = "<code>sets[%d][%s]</code>??"                                 Fault( string.format( s, k, ek ) )                             end                         end -- for ek, ev                     else                         k = mw.text.nowiki( tostring( k ) )                         v = mw.text.nowiki( tostring( v ) )                         s = string.format( "<code>sets[%s][%s]</code>??",                                            k, v )                         Fault( s )                     end                 else                     k = mw.text.nowiki( tostring( k ) )                     s = string.format( "<code>sets[%s]</code> ?????", k )                     Fault( s )                 end             end -- for k, v         else             s = "<code>params</code> required for <code>sets</code>"             Fault( s )         end     else         s = "<code>sets</code> needs to be of <code>object</code> type"         Fault( s )     end end -- fellows()    local function finalize( advance )     -- Wrap presentation into frame     -- Parameter:     --     advance  -- true, for nice     -- Returns string     local r, lapsus     if Data.div then         r = tostring( Data.div )     elseif Data.strip then         r = Data.strip     else         lapsus = true         r      = ""     end     r = r .. failures()     if Data.source then         local live = ( advance or lapsus )         if not live then             live = TemplateData.frame:preprocess( "{{REVISIONID}}" )             live = ( live == "" )         end         if live then             r = r .. fancy( advance, lapsus )         end     end     return r end -- finalize()    local function find()     -- Find JSON data within page source (title)     -- Returns string, or nil     local s = Data.title:getContent()     local i, j = s:find( "<templatedata>", 1, true )     local r     if i then         local k = s:find( "</templatedata>", j, true )         if k then            r = mw.text.trim( s:sub( j + 1,  k - 1 ) )         end     end     return r end -- find()    local function flat( adjust )     -- Remove formatting from text string for VE     -- Parameter:     --     arglist  -- string, to be stripped, or nil     -- Returns string, or nil     local r     if adjust then         r = adjust:gsub( "\n", " " )         if r:find( "<noexport>", 1, true ) then             r = r:gsub( "<noexport>.*</noexport>", "" )         end         if r:find( "<exportonly>", 1, true ) then             r = r:gsub( "</?exportonly>", "" )         end         if r:find( "''", 1, true ) then             r = r:gsub( "'''", "" ):gsub( "''", "" )         end         if r:find( "<", 1, true ) then             local Text = Fetch( "Text" )             r = Text.getPlain( r:gsub( "<br */?>", "\r\n" ) )         end         if r:find( "[", 1, true ) then             local WLink = Fetch( "WLink" )             if WLink.isBracketedURL( r ) then                 r = r:gsub( "%[([hf]tt?ps?://%S+) [^%]]+%]", "%1" )             end             r = WLink.getPlain( r )         end         if r:find( "&", 1, true ) then             r = mw.text.decode( r )             if r:find( "&shy;", 1, true ) then                 r = r:gsub( "&shy;", "" )             end         end     end     return r end -- flat()    local function flush()     -- JSON encode narrowed input; obey unnamed (numerical) parameters     -- Returns <templatedata> JSON string     local r     if Data.tag then         r = mw.text.jsonEncode( Data.tag ):gsub( "%}$", "," )     else         r = "{"     end     r = r .. "\n\"params\":{"     if Data.order then         local sep = ""         local s         for i = 1, #Data.order do             s   = Data.order[ i ]             r   = string.format( "%s%s\n%s:%s",                                  r,                                  sep,                                  mw.text.jsonEncode( s ),                                  mw.text.jsonEncode( Data.params[ s ] ) )             sep = ",\n"         end -- for i = 1, #Data.order     end     r = r .. "\n}\n}"     return r end -- flush()    local function focus( access )     -- Check components; focus multilingual description, build trees     -- Parameter:     --     access  -- string, name of parameter, nil for root     local f = function ( a, at )                     local r                     if at then                         r = string.format( "<code>params.%s</code>", at )                     else                         r = "''root''"                     end                     if a then                         r = string.format( "%s<code>.%s</code>", r, a )                     end                     return r                 end     local parent     if access then         parent = Data.got.params[ access ]     else         parent = Data.got     end     if type( parent ) == "table" then         local elem, got, permit, s, scope, slot, tag, target         if access then             permit = Permit.params             if type( access ) == "number" then                 slot = tostring( access )             else                 slot = access             end         else             permit = Permit.root         end         for k, v in pairs( parent ) do             scope = permit[ k ]             if scope then                 s = type( v )                 if s == "string"  and  k ~= "format" then                     v = mw.text.trim( v )                 end                 if scope:find( s, 1, true ) then                     if scope:find( "I18N", 1, true ) then                         if s == "string" then                             elem = fair( v )                         elseif s == "table" then                             local translated                             v, translated = faraway( v )                             if v then                                 if translated  and                                    k == "description" then                                     elem = { [ 1 ] = fair( v ),                                              [ 2 ] = translated }                                 else                                     elem = fair( v )                                 end                             else                                 elem = false                             end                         end                         if type( v ) == "string" then                             if k == "deprecated" then                                 if v == "1" then                                     v = true                                 elseif v == "0" then                                     v = false                                 end                                 elem = v                             elseif scope:find( "nowiki", 1, true ) then                                 elem = mw.text.nowiki( v )                                 elem = elem:gsub( "&#13;\n", "<br>" )                                 v    = v:gsub( string.char( 13 ),  "" )                             else                                 v = flat( v )                             end                         elseif s == "boolean" then                             if scope:find( "boolean", 1, true ) then                                 elem = v                             else                                 s = "Type <code>boolean</code> bad for "                                     .. f( k, slot )                                 Fault( s )                             end                         end                     else                         if k == "params"  and  not access then                             v    = nil                             elem = nil                         elseif k == "format"  and  not access then                             elem = mw.text.decode( v )                             v    = nil                         elseif k == "inherits" then                             elem = v                             if not Data.heirs then                                 Data.heirs = { }                             end                             Data.heirs[ slot ] = v                             v                  = nil                         elseif k == "style" then                             elem = v                             v    = nil                         elseif s == "string" then                             v    = mw.text.nowiki( v )                             elem = v                         else                             elem = v                         end                     end                     if type( elem ) ~= "nil" then                         if not target then                             if access then                                 if not Data.tree.params then                                     Data.tree.params = { }                                 end                                 Data.tree.params[ slot ] = { }                                 target = Data.tree.params[ slot ]                             else                                 Data.tree = { }                                 target    = Data.tree                             end                         end                         target[ k ] = elem                         elem        = false                     end                     if type( v ) ~= "nil" then                         if not tag then                             if access then                                 if type( v ) == "string"  and                                    v.sub( 1, 1 ) == "=" then                                     v = nil                                 else                                     if not Data.params then                                         Data.params = { }                                     end                                     Data.params[ slot ] = { }                                     tag = Data.params[ slot ]                                 end                             else                                 Data.tag = { }                                 tag      = Data.tag                             end                         end                         if type( v ) ~= "nil"  and                            k ~= "suggestedvalues" then                             tag[ k ] = v                         end                     end                 else                     s = string.format( "Type <code>%s</code> bad for %s",                                        scope,  f( k, slot ) )                     Fault( s )                 end             else                 Fault( "Unknown component " .. f( k, slot ) )             end         end -- for k, v         if not access  and Data.got.sets then             fellows()         end     else         Fault( f() .. " needs to be of <code>object</code> type" )     end end -- focus()    local function format()     -- Build formatted element     -- Returns <inline>     local source = Data.tree.format:lower()     local r, s     if source == "inline"  or  source == "block" then         r = mw.html.create( "i" )                    :wikitext( source )     else         local code         if source:find( "|", 1, true ) then             local scan = "^[\n ]*%{%{[\n _]*|[\n _]*=[\n _]*%}%}[\n ]*$"             if source:match( scan ) then                 code = source:gsub( "\n", "N" )             else                 s = mw.text.nowiki( source ):gsub( "\n", "&#92;n" )                 s = tostring( mw.html.create( "code" )                                      :wikitext( s ) )                 Fault( "Invalid format " .. s )                 source = false             end         else             local words = mw.text.split( source, "%s+" )             local show, start, support, unknown             for i = 1, #words do                 s = words[ i ]                 if i == 1 then                     start = s                 end                 support = Permit.builder[ s ]                 if support == start  or                    support == "*" then                     Permit.builder[ s ] = true                 elseif s:match( "^[1-9]%d?" ) and                        Permit.builder.align then                     Permit.builder.align = tonumber( s )                 else                     if unknown then                         unknown = string.format( "%s %s", unknown, s )                     else                         unknown = s                     end                 end             end -- i = 1, #words             if unknown then                 s = tostring( mw.html.create( "code" )                                      :css( "white-space", "nowrap" )                                      :wikitext( s ) )                 Fault( "Unknown/misplaced format keyword " .. s )                 source = false                 start  = false             end             if start == "inline" then                 if Permit.builder.half == true then                     show = "inline half"                     code = "{{_ |_=_}}"                 elseif Permit.builder.grouped == true then                     show = "inline grouped"                     code = "{{_ | _=_}}"                 elseif Permit.builder.spaced == true then                     show = "inline spaced"                     code = "{{_ | _ = _ }}"                 end                 if Permit.builder.newlines == true then                     show = show or "inline"                     code = code or "{{_|_=_}}"                     show = show .. " newlines"                     code = string.format( "N%sN", code )                 end             elseif start == "block" then                 local space  = ""     -- amid "|" and name                 local spaced = " "    -- preceding "="                 local spacer = " "    -- following "="                 local suffix = "N"    -- closing "}}" on new line                 show = "block"                 if Permit.builder.indent == true then                     start = " "                     show = "block indent"                 else                     start = ""                 end                 if Permit.builder.compressed == true then                     spaced = ""                     spacer = ""                     show   = show .. " compressed"                     if Permit.builder.last == true then                         show = show .. " last"                     else                         suffix = ""                     end                 else                     if Permit.builder.lead == true then                         show  = show .. " lead"                         space = " "                     end                     if type( Permit.builder.align ) ~= "string" then                         local n                         s = " align"                         if Permit.builder.align == true then                             n = 0                             if type( Data.got ) == "table"  and                                type( Data.got.params ) == "table" then                                 for k, v in pairs( Data.got.params ) do                                     if type( v ) == "table"  and                                        not v.deprecated  and                                        type( k ) == "string" then                                         k = mw.ustring.len( k )                                         if k > n then                                             n = k                                         end                                     end                                 end -- for k, v                             end                         else                             n = Permit.builder.align                             if type( n ) == "number"  and  n > 1 then                                 s = string.format( "%s %d", s, n )                             else                                 n = 0    -- How comes?                             end                         end                         if n > 1 then                             spaced = string.rep( "_",  n - 1 )  ..  " "                         end                         show = show .. s                     elseif Permit.builder.after == true then                         spaced = ""                         show   = show .. " after"                     elseif Permit.builder.dense == true then                         spaced = ""                         spacer = ""                         show   = show .. " dense"                     end                     if Permit.builder.last == true then                         suffix = spacer                         show   = show .. " last"                     end                 end                 code = string.format( "N{{_N%s|%s_%s=%s_%s}}N",                                       start,                                       space,                                       spaced,                                       spacer,                                       suffix )                 if show == "block" then                     show = "block newlines"                 end             end             if show then                 r = mw.html.create( "span" )                            :wikitext( show )             end         end         if code then             source = code:gsub( "N", "\n" )             code   = mw.text.nowiki( code ):gsub( "N", "&#92;n" )             code   = mw.html.create( "code" )                             :css( "margin-left",  "1em" )                             :css( "margin-right", "1em" )                             :wikitext( code )             if r then                 r = mw.html.create( "span" )                            :node( r )                            :node( code )             else                 r = code             end         end     end     if source and Data.tag then         Data.tag.format = source     end     return r end -- format()    local function formatter()     -- Build presented documentation     -- Returns <div>     local r = mw.html.create( "div" )     local x = fashioned( Data.tree, true, r )     local s     if x then         r = x     end     if Data.leading then         local toc = mw.html.create( "div" )         local shift         if Config.suppressTOCnum then             toc:addClass( Config.suppressTOCnum )             if type( Config.stylesTOCnum ) == "string" then                 local src = Config.stylesTOCnum .. "/styles.css"                 s = TemplateData.frame:extensionTag( "templatestyles",                                                      nil,                                                      { src = src } )                 r:newline()                  :node( s )             end         end         toc:css( "margin-top", "0.5em" )            :wikitext( "__TOC__" )         if Data.sibling then             local block = mw.html.create( "div" )             if TemplateData.ltr then                 shift = "right"             else                 shift = "left"             end             block:css( "float", shift )                  :wikitext( Data.sibling )             r:newline()              :node( block )              :newline()         end         r:newline()          :node( toc )          :newline()         if shift then             r:node( mw.html.create( "div" )                            :css( "clear", shift ) )              :newline()         end     end     s = features()     if s then         if Data.leading then             r:node( mw.html.create( "h" .. Config.nested )                            :wikitext( factory( "doc-params" ) ) )              :newline()         end         r:node( s )     end     if Data.shared then         local global = mw.html.create( "div" )                               :attr( "id", "templatedata-global" )         local shift         if TemplateData.ltr then             shift = "right"         else             shift = "left"         end         global:css( "float", shift )               :wikitext( string.format( "[[%s|%s]]",                                         Data.shared, "Global" ) )         r:newline()          :node( global )     end     if Data.tree and Data.tree.format then         local e = format()         if e then             local show = "Format"             if Config.supportFormat then                 show = string.format( "[[%s|%s]]",                                       Config.supportFormat, show )             end             r:node( mw.html.create( "p" )                            :wikitext( show .. ": " )                            :node( e ) )         end     end     return r end -- formatter()    local function free()     -- Remove JSON comment lines     if Data.source:find( "//", 1, true ) then         Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([{},\"'])",                           "%1%3" )     end end -- free()    local function full()     -- Build survey table from JSON data, append invisible <templatedata>     Data.div = mw.html.create( "div" )                       :addClass( "mw-templatedata-doc-wrap" )     focus()     if Data.tag then         if type( Data.got.params ) == "table" then             for k, v in pairs( Data.got.params ) do                 focus( k )             end -- for k, v             if Data.heirs then                 fathers()             end         end     end     Data.div:node( formatter() )     if not Data.lazy then         Data.slim = flush()         if TemplateData.frame then             local div   = mw.html.create( "div" )             local tdata = { [ 1 ] = "templatedata",                             [ 2 ] = Data.slim }             Data.strip = TemplateData.frame:callParserFunction( "#tag",                                                                 tdata )             div:wikitext( Data.strip )             if Config.loudly then                 Data.div:node( mw.html.create( "hr" )                                       :css( { height = "7ex" } ) )             else                 div:css( "display", "none" )             end             Data.div:node( div )         end     end     if Data.lasting then         Fault( "deprecated type syntax" )     end     if Data.less then         Fault( Config.solo )     end end -- full()    local function furnish( adapt, arglist )     -- Analyze transclusion     -- Parameter:     --     adapt    -- table, #invoke parameters     --     arglist  -- table, template parameters     -- Returns string     local source     favorize()     -- deprecated:     for k, v in pairs( Config.basicCnf ) do         if adapt[ k ]  and  adapt[ k ] ~= "" then             Config[ v ] = adapt[ k ]         end     end -- for k, v     if arglist.heading  and  arglist.heading:match( "^[3-6]$" ) then         Config.nested = arglist.heading     else         Config.nested = "2"     end     Config.loudly = faculty( arglist.debug or adapt.debug )     Data.lazy     = faculty( arglist.lazy )  and  not Config.loudly     Data.leading  = faculty( arglist.TOC )     if Data.leading and arglist.TOCsibling then         Data.sibling = mw.text.trim( arglist.TOCsibling )     end     if arglist.lang then         Data.slang = arglist.lang:lower()     elseif adapt.lang then         Data.slang = adapt.lang:lower()     end     if arglist.JSON then         source = arglist.JSON     elseif arglist.Global then         source = TemplateData.getGlobalJSON( arglist.Global,                                              arglist.Local )     elseif arglist[ 1 ] then         local s     = mw.text.trim( arglist[ 1 ] )         local start = s:sub( 1, 1 )         if start == "<" then             Data.strip = s         elseif start == "{" then             source = s         elseif mw.ustring.sub( s, 1, 8 ) ==                mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then             Data.strip = s         end     end     if type( arglist.vertical ) == "string"  and        arglist.vertical:match( "^%d*%.?%d+[emprx]+$" ) then         Data.scroll = arglist.vertical     end     if not source then         Data.title = mw.title.getCurrentTitle()         source = find()         if not source  and            not Data.title.text:match( Config.subpage ) then             local s = string.format( Config.suffix,                                      Data.title.prefixedText )             Data.title = mw.title.new( s )             if Data.title.exists then                 source = find()             end         end     end     if not Data.lazy then         if not Data.title then             Data.title = mw.title.getCurrentTitle()         end         Data.lazy = Data.title.text:match( Config.subpage )     end     if type( source ) == "string" then         TemplateData.getPlainJSON( source )     end     return finalize( faculty( arglist.source ) ) end -- furnish()    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()    TemplateData.getGlobalJSON = function ( access, adapt )     -- Retrieve TemplateData from a global repository (JSON)     -- Parameter:     --     access  -- string, with page specifier (on WikiMedia Commons)     --     adapt   -- JSON string or table with local overrides     -- Returns true, if succeeded     local plugin = Fetch( "/global" )     local r     if type( plugin ) == "table"  and        type( plugin.fetch ) == "function" then         local s, got = plugin.fetch( access, adapt )         if got then             Data.got    = got             Data.order  = got.paramOrder             Data.shared = s             r           = true             full()         else             Fault( s )         end     end     return r end -- TemplateData.getGlobalJSON()    TemplateData.getPlainJSON = function ( adapt )     -- Reduce enhanced JSON data to plain text localized JSON     -- Parameter:     --     adapt  -- string, with enhanced JSON     -- Returns string, or not     if type( adapt ) == "string" then         local JSONutil = Fetch( "JSONutil", true )         Data.source = adapt         free()         if JSONutil then             local Multilingual = Fetch( "Multilingual", true )             local f             if Multilingual then                 f = Multilingual.i18n             end             Data.got = JSONutil.fetch( Data.source, true, f )         else             local lucky             lucky, Data.got = pcall( mw.text.jsonDecode, Data.source )         end         if type( Data.got ) == "table" then             full()         elseif not Data.strip then             local scream = type( Data.got )             if scream == "string" then                 scream = Data.got             else                 scream = "Data.got: " .. scream             end             Fault( "fatal JSON error: " .. scream )         end     end     return Data.slim end -- TemplateData.getPlainJSON()    TemplateData.test = function ( adapt, arglist )     TemplateData.frame = mw.getCurrentFrame()     return furnish( adapt, arglist ) end -- TemplateData.test()    -- Export local p = { }  p.f = function ( frame )     -- Template call     local lucky, r     TemplateData.frame = frame     lucky, r = pcall( furnish, frame.args, frame:getParent().args )     if not lucky then         Fault( "INTERNAL: " .. r )         r = failures()     end     return r end -- p.f  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  p.TemplateData = function ()     -- Module interface     return TemplateData end  return p