Modul:TemplateData: Unterschied zwischen den Versionen

Aus skandinavien-wiki.net
w>PerfektesChaos
(2019-07-02)
K (73 Versionen von wikivoyage:Modul:TemplateData importiert)
 
(13 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
local TemplateData = { suite  = "TemplateData",
local TemplateData = { suite  = "TemplateData",
                       serial = "2019-07-02",
                       serial = "2021-07-05",
                       item  = 46997995 }
                       item  = 46997995 }
--[=[
--[==[
improve template:TemplateData
improve template:TemplateData
]=]
]==]
local Failsafe = TemplateData




Zeile 12: Zeile 13:
     basicCnf = { catProblem    = "strange",
     basicCnf = { catProblem    = "strange",
                 classNoNumTOC = "suppressTOCnum",
                 classNoNumTOC = "suppressTOCnum",
                classTable    = "classTable",
                 cssParWrap    = "cssTabWrap",
                 cssParWrap    = "cssTabWrap",
                 cssParams    = "cssTable",
                 cssParams    = "cssTable",
Zeile 30: Zeile 32:
                 msgDescMiss  = "solo",
                 msgDescMiss  = "solo",
                 tStylesTOCnum = "stylesTOCnum" },
                 tStylesTOCnum = "stylesTOCnum" },
--  classParams    = "classTable",
    classTable    = { "wikitable" },    -- classes for params table
--  classTable    = false,    -- class for params table
     debugmultilang = "C0C0C0",
     debugmultilang = "C0C0C0",
     loudly        = false,    -- show exported element, etc.
     loudly        = false,    -- show exported element, etc.
Zeile 48: Zeile 49:
     got    = false,    -- table, initial templatedata object
     got    = false,    -- table, initial templatedata object
     heirs  = false,    -- table, params that are inherited
     heirs  = false,    -- table, params that are inherited
    jump    = false,    -- source position at end of "params"
     less    = false,    -- main description missing
     less    = false,    -- main description missing
     lasting = false,    -- old syntax encountered
     lasting = false,    -- old syntax encountered
Zeile 66: Zeile 68:
}
}
local Permit = {
local Permit = {
     builder = { after     = "block",
     builder = { after           = "block",
                 align     = "block",
                 align           = "block",
                 block     = "block",
                 block           = "block",
                 compressed = "block",
                 compressed     = "block",
                 dense     = "block",
                 dense           = "block",
                 grouped   = "inline",
                 grouped         = "inline",
                 half       = "inline",
                 half           = "inline",
                 indent     = "block",
                 indent         = "block",
                 inline     = "inline",
                 inline         = "inline",
                 last       = "block",
                 last           = "block",
                 lead       = "block",
                 lead           = "block",
                 newlines   = "*",
                 newlines       = "*",
                 spaced     = "inline" },
                 spaced         = "inline" },
     colors  = { tableheadbg = "B3B7FF",
     colors  = { tableheadbg = "B3B7FF",
                 required    = "EAF3FF",
                 required    = "EAF3FF",
Zeile 84: Zeile 86:
                 optional    = "EAECF0",
                 optional    = "EAECF0",
                 deprecated  = "FFCBCB" },
                 deprecated  = "FFCBCB" },
     params  = { aliases     = "table",
     params  = { aliases         = "table",
                 autovalue   = "string",
                 autovalue       = "string",
                 default     = "string table I18N nowiki",
                 default         = "string table I18N nowiki",
                 deprecated = "boolean string I18N",
                 deprecated     = "boolean string I18N",
                 description = "string table I18N",
                 description     = "string table I18N",
                 example     = "string table I18N nowiki",
                 example         = "string table I18N nowiki",
                 label       = "string table I18N",
                 label           = "string table I18N",
                 inherits   = "string",
                 inherits       = "string",
                 required   = "boolean",
                 required       = "boolean",
                 style       = "string table",
                 style           = "string table",
                 suggested   = "boolean",
                 suggested       = "boolean",
                 type       = "string" },
                suggestedvalues = "string table number",
                 type           = "string" },
     root    = { description = "string table I18N",
     root    = { description = "string table I18N",
                 format      = "string",
                 format      = "string",
Zeile 171: Zeile 174:
             TemplateData.extern[ sign ] = r
             TemplateData.extern[ sign ] = r
         elseif not allow then
         elseif not allow then
             error( string.format( "Fetch(%s) %s", sign, g ) )
             error( string.format( "Fetch(%s) %s", sign, g ), 0 )
         end
         end
     end
     end
Zeile 211: Zeile 214:
                                   :gsub( "([%-.()+*?^$%[%]])",
                                   :gsub( "([%-.()+*?^$%[%]])",
                                           "%%%1" ) )
                                           "%%%1" ) )
     local i, k = Data.source:find( seek, at )
     local i, k, r, slice, source
     local 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
     while i  and  not r do
         source = Data.source:sub( k + 1 )
         source = Data.source:sub( k + 1 )
Zeile 223: Zeile 234:
             r = k
             r = k
         else
         else
             i, k = Data.source:find( seek, k )
             i, k = Data.source:find( seek, k )
         end
         end
     end    -- while i
     end    -- while i
Zeile 291: Zeile 302:
     --    adjust  -- string
     --    adjust  -- string
     -- Returns string, with adjusted text
     -- Returns string, with adjusted text
     local f = function ( a )
     local f   = function ( a )
                  return a:gsub( "%s*\n%s*", " " )
                    return a:gsub( "%s*\n%s*", " " )
                          :gsub( "%s%s+", " " )
                            :gsub( "%s%s+", " " )
              end
                end
     local r
     local tags = { { start = "<noexport>",
    if adjust:find( "<noexport>", 1, true ) then
                    stop  = "</noexport>" },
        local i    = 1
                  { start = "<exportonly>",
        local j, k = adjust:find( "<noexport>", i, true )
                    stop  = "</exportonly>",
        r = ""
                    l    = false }
        while j do
                }
            if j > 1 then
    local r = adjust
                r = r .. f( adjust:sub( i, j - 1 ) )
    local i, j, k, s, tag
             end
    for m = 1, 2 do
             i = k + 1
        tag = tags[ m ]
             j, k = adjust:find( "</noexport>", i, true )
        if r:find( tag.start, 1, true ) then
             if j then
            s    = r
                r   = r .. adjust:sub( i,  j - 1 )
             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
                 i    = k + 1
                 j, k = adjust:find( "<noexport>", i, true )
                 j, k = s:find( tag.stop, i, true )
            else
                if j then
                Fault( "missing </noexport>" )
                    if m == 1 then
            end
                        r = r .. s:sub( i,  j - 1 )
        end    -- while j
                    end
        r = r .. adjust:sub( i )
                    i    = k + 1
     else
                    j, k = s:find( tag.start, i, true )
         r = f( adjust )
                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
     end
     return r
     return r
end -- fair()
end -- fair()




Zeile 381: Zeile 408:
         if type( v ) == "string" then
         if type( v ) == "string" then
             v = mw.text.trim( v )
             v = mw.text.trim( v )
             if v ~= "" then
             if v ~= ""  and  type( k ) == "string" then
                 k = k:lower()
                 k = k:lower()
                 variants[ k ] = v
                 variants[ k ] = v
Zeile 649: Zeile 676:




local function feat()
local function feasible( about, at )
     -- Check and store parameter sequence
     -- Deal with suggestedvalues within parameter
     if Data.source then
    -- Parameter:
        local i = 0
    --    about  -- parameter details
        local s
     --              .suggestedvalues  -- table|string|number,
        for k, v in pairs( Data.tree.params ) do
    --                                    value and possibly description
            if i == 0 then
    --                                    .code  -- mandatory
                Data.order = { }
    --                                    .label  -- table|string
                i = 1
    --                                    .icon  -- string
                s = k
    --                                    .class  -- table|string
            else
    --                                    .css    -- table
                i = 2
    --                                    .style  -- string
                break -- for k, v
    --                                    .less  -- true: suppress code
            end
    --              .type
        end -- for k, v
    --    at    -- string, with parameter name
        if i > 1 then
    -- Returns
            local pointers = { }
    --    1: mw.html object
            local points  = { }
    --     2: sequence table with values, or nil
            local given    = { }
    local p = about.suggestedvalues
             for k, v in pairs( Data.tree.params ) do
    local s = type( p )
                 i = facet( k, 1 )
    local e, r1, r2, v
                 if type( v ) == "table" then
    if s == "table" then
                     if type( v.label ) == "string" then
        if #p > 0 then
                         s = mw.text.trim( v.label )
             for i = 1, #p do
                         if s == "" then
                 e = p[ i ]
                             s = k
                s = type( e )
                         end
                 if s == "table" then
                     else
                     if type( e.code ) == "string" then
                         s = k
                         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
                     end
                     if given[ s ] then
                elseif s == "string" then
                        if given[ s ] == 1 then
                     s = mw.text.trim( e )
                            local scream = "Parameter label '%s' detected multiple times"
                    if s == "" then
                            Fault( string.format( scream, s ) )
                        e = nil
                            given[ s ] = 2
                        s = string.format( "params.%s.%s[%d] EMPTY",
                         end
                                          at, "suggestedvalues", i )
                         Fault( s )
                     else
                     else
                         given[ s ] = 1
                         e = { code = s }
                     end
                     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
                 end
                 if i then
                 if e then
                     table.insert( points, i )
                     v = v  or  { }
                    pointers[ i ] = k
                     table.insert( v, e )
                     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
             end -- for k, v
             end -- for i
             table.sort( points )
        else
            for i = 1, #points do
             Fault( string.format( "params.%s.suggestedvalues %s",
                table.insert( Data.order,  pointers[ points[ i ] ] )
                  at, "NOT AN ARRAY" ) )
            end -- i = 1, #points
        end
         elseif s then
    elseif s == "string" then
             table.insert( Data.order, s )
        s = mw.text.trim( p )
         if s ~= "" then
            v = { }
             table.insert( v,
                          { code = s } )
         end
         end
    elseif s == "number" then
        v = { }
        table.insert( v,
                      { code = tostring( p ) } )
     end
     end
end -- feat()
    if v then
 
        local d, less, story, swift, t, u
 
        r1 = mw.html.create( "ul" )
 
        r2 = { }
local function feature( access )
        for i = 1, #v do
    -- Create table row for parameter, check and display violations
            u = mw.html.create( "li" )
    -- Parameter:
            e = v[ i ]
    --    access -- string, with name
            table.insert( r2, e.code )
    -- Returns <tr>
            story = false
    local mode, s, status
            less = ( e.less == true )
    local fine    = function ( a )
            if not less then
                         s = mw.text.trim( a )
                swift = e.code
                         return a == s  and
                if e.support then
                              a ~= "" and
                    local scream, support
                              not a:find( "%|=\n" )  and
                    s = type( e.support )
                              not a:find( "%s%s" )
                    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
    local begin  = mw.html.create( "td" )
                end
    local code    = mw.html.create( "code" )
                u:node( mw.html.create( "code" )
    local desc    = mw.html.create( "td" )
                              :css( "white-space", "nowrap" )
    local legal  = true
                              :wikitext( swift ) )
    local param  = Data.tree.params[ access ]
            end
    local ranking = { "required", "suggested", "optional", "deprecated" }
            if e.class then
    local r      = mw.html.create( "tr" )
                s = type( e.class )
    local sort, typed
                if s == "string" then
 
                    u:addClass( e.class )
    for k, v in pairs( param ) do
                elseif s == "table" then
        if v == "" then
                    for k, s in pairs( e.class ) do
            param[ k ] = false
                        u:addClass( s )
        end
                    end -- for k, s
    end -- for k, v
                else
 
                    s = string.format( "params.%s.%s[%d].class INVALID",
    -- label
                                      at, "suggestedvalues", i )
    sort = param.label or access
                    Fault( s )
    if sort:match( "^%d+$" ) then
                end
        begin:attr( "data-sort-value",
            end
                     string.format( "%05d", tonumber( sort ) ) )
            if e.css then
     end
                if type( e.css ) == "table" then
     begin:css( "font-weight", "bold" )
                    u:css( e.css )
        :wikitext( sort )
                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()
 
 


    -- name and aliases
local function feat()
    code:css( "font-size", "92%" )
     -- Check and store parameter sequence
        :css( "white-space", "nowrap" )
     if Data.source then
        :wikitext( access )
         local i = 0
     if not fine( access ) then
         local s
        code:addClass( "error" )
         for k, v in pairs( Data.tree.params ) do
        Fault( string.format( "Bad ID params.<code>%s</code>", access ) )
             if i == 0 then
        legal = false
                 Data.order = { }
        begin:attr( "data-sort-value",  " " .. sort )
                 i = 1
     end
                 s = k
    code = mw.html.create( "td" )
                  :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
         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" ) )
                 end
                 code:wikitext( s )
             else
             else
                 lapsus = true
                 i = 2
                 code:node( mw.html.create( "code" )
                 break -- for k, v
                                  :addClass( "error" )
                                  :wikitext( type( v ) ) )
             end
             end
         end -- for k, v
         end -- for k, v
         if lapsus then
         if i > 1 then
             s = string.format( "params.<code>%s</code>.aliases", access )
             local pointers = { }
             Fault(  factory( "invalid-value" ):gsub( "$1", s )  )
             local points  = { }
             legal = false
             local given    = { }
        end
            for k, v in pairs( Data.tree.params ) do
    end
                i = facet( k, 1 )
 
                if type( v ) == "table" then
    -- description etc.
                    if type( v.label ) == "string" then
    s = fashioned( param )
                        s = mw.text.trim( v.label )
    if s then
                        if s == "" then
        desc:node( s )
                            s = k
    end
                        end
    if param.style then
                     else
        s = type( param.style )
                         s = k
        if s == "table" then
            desc:css( param.style )
        elseif s == "string" then
            desc:cssText( param.style )
        end
    end
    if param.default or param.example or param.autovalue then
        local details = { "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
                     end
                     if type( boole.show ) == "string" then
                     if given[ s ] then
                        local v = mw.html.create( "span" )
                        if given[ s ] == 1 then
                                        :wikitext( boole.show )
                            local scream = "Parameter label '%s' detected multiple times"
                        if boole.css then
                            Fault( string.format( scream, s ) )
                             v:css( boole.css )
                             given[ s ] = 2
                         end
                         end
                         dd:node( v )
                    else
                         given[ s ] = 1
                     end
                     end
                     if type( boole.suffix ) == "string" then
                end
                        dd:wikitext( boole.suffix )
                if i then
                    end
                     table.insert( points, i )
                     if boole.lead == false then
                    pointers[ i ] = k
                         dd:wikitext( " " )
                    i = facet( k, i )
                          :node( mw.html.create( "code" )
                     if i then
                                        :wikitext( show ) )
                         s = "Parameter '%s' detected twice"
                        Fault( string.format( s, k ) )
                     end
                     end
                 else
                 else
                     dd:wikitext( show )
                     s = "Parameter '%s' not detected"
                    Fault( string.format( s, k ) )
                 end
                 end
                dl:node( mw.html.create( "dt" )
            end -- for k, v
                                :wikitext( section ) )
            table.sort( points )
                  :node( dd )
            for i = 1, #points do
             end
                table.insert( Data.order,  pointers[ points[ i ] ] )
        end -- i = 1, #details
             end -- i = 1, #points
         desc:node( dl )
         elseif s then
            table.insert( Data.order, s )
        end
     end
     end
end -- feat()


    -- type
 
    if type( param.type ) == "string" then
 
        param.type = mw.text.trim( param.type )
local function feature( access )
        if param.type == "" then
    -- Create table row for parameter, check and display violations
            param.type = false
    -- Parameter:
        end
    --    access  -- string, with name
     end
     -- Returns <tr>
     if param.type then
     local mode, s, status
        s     = Permit.types[ param.type ]
     local fine    = function ( a )
        typed = mw.html.create( "td" )
                        s = mw.text.trim( a )
        if s then
                        return a == s and
            if s == "string" then
                              a ~= "" and
                Data.params[ access ].type = s
                              not a:find( "%|=\n" ) and
                typed:wikitext( factory( "doc-param-type-" .. s ) )
                              not a:find( "%s%s" )
                    :tag( "br" )
                    end
                typed:node( mw.html.create( "span" )
    local begin  = mw.html.create( "td" )
                                  :addClass( "error" )
    local code    = mw.html.create( "code" )
                                  :wikitext( param.type ) )
    local desc    = mw.html.create( "td" )
                Data.lasting = true
    local eager  = mw.html.create( "td" )
            else
    local legal  = true
                local support = Config[ "support4" .. param.type ]
    local param  = Data.tree.params[ access ]
                s = factory( "doc-param-type-" .. param.type )
    local ranking = { "required", "suggested", "optional", "deprecated" }
                if support then
    local r      = mw.html.create( "tr" )
                    s = string.format( "[[%s|%s]]", support, s )
    local styles = "mw-templatedata-doc-param-"
                end
    local sort, typed
                typed:wikitext( s )
 
            end
    for k, v in pairs( param ) do
        else
        if v == "" then
            Data.params[ access ].type = "unknown"
             param[ k ] = false
            typed:addClass( "error" )
                :wikitext( "INVALID" )
            s = string.format( "params.<code>%s</code>.type", access )
            Fault( factory( "invalid-value" ):gsub( "$1", s )  )
             legal = false
         end
         end
     else
     end -- for k, v
        typed = mw.html.create( "td" )
 
                  :wikitext( factory( "doc-param-type-unknown" ) )
    -- label
    sort = param.label or access
    if sort:match( "^%d+$" ) then
        begin:attr( "data-sort-value",
                    string.format( "%05d", tonumber( sort ) ) )
     end
     end
    begin:css( "font-weight", "bold" )
        :wikitext( sort )


     -- status
     -- name and aliases
     if param.required then
     code:css( "font-size", "92%" )
         mode = 1
         :css( "white-space", "nowrap" )
         if param.autovalue then
         :wikitext( access )
            Fault( string.format( "autovalued <code>%s</code> required",
    if not fine( access ) then
                                  access ) )
        code:addClass( "error" )
            legal = false
        Fault( string.format( "Bad ID params.<code>%s</code>", access ) )
         end
        legal = false
         if param.default then
         begin:attr( "data-sort-value",  " " .. sort )
             Fault( string.format( "Defaulted <code>%s</code> required",
    end
                                   access ) )
    code = mw.html.create( "td" )
             legal = false
                  :addClass( styles .. "name" )
         end
                  :node( code )
         if param.deprecated then
    if access:match( "^%d+$" ) then
             Fault( string.format( "Required deprecated <code>%s</code>",
         code:attr( "data-sort-value",
                                  access ) )
                  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
             legal = false
         end
         end
    elseif param.deprecated then
        mode = 4
    elseif param.suggested then
        mode = 2
    else
        mode = 3
     end
     end
     status = ranking[ mode ]
 
     ranking = factory( "doc-param-status-" .. status )
     -- description etc.
     if mode == 1  or  mode == 4 then
     s = fashioned( param )
         ranking = mw.html.create( "span" )
     if s then
                        :css( "font-weight", "bold" )
         desc:node( s )
                        :wikitext( ranking )
    end
         if type( param.deprecated ) == "string" then
    if param.style then
             ranking:tag( "br" )
         s = type( param.style )
             ranking:wikitext( param.deprecated )
        if s == "table" then
             desc:css( param.style )
        elseif s == "string" then
             desc:cssText( param.style )
         end
         end
     end
     end
 
     if param.suggestedvalues or
     -- <tr>
      param.default or
    r:attr( "id",  "templatedata:" .. mw.uri.anchorEncode( access ) )
      param.example or
    :css( Permit.css[ status ] )
      param.autovalue then
    :node( begin )
        local details = { "suggestedvalues",
    :node( code )
                          "default",
    :node( desc )
                          "example",
    :node( typed )
                          "autovalue" }
     :node( mw.html.create( "td" )
        local dl     = mw.html.create( "dl" )
                  :attr( "data-sort-value", tostring( mode ) )
        local dd, section, show
                  :node( ranking ) )
         for i = 1, #details do
    :newline()
            s    = details[ i ]
    if not legal then
            show = param[ s ]
         r:css( "border", "#FF0000 3px solid" )
            if show then
    end
                dd      = mw.html.create( "dd" )
    return r
                section = factory( "doc-param-" .. s )
end -- feature()
                if param.type == "boolean"  and
 
                  ( show == "0" or show == "1" ) then
 
                    local boole = Permit.boole[ ( show == "1" ) ]
 
                    if boole.lead == true then
local function features()
                        dd:node( mw.html.create( "code" )
    -- Create <table> for parameters
                                        :wikitext( show ) )
    -- Returns <table>, or nil
                          :wikitext( " " )
    local r
                    end
    if Data.tree and Data.tree.params then
                    if type( boole.show ) == "string" then
        local tbl = mw.html.create( "table" )
                        local v = mw.html.create( "span" )
                          :addClass( "wikitable" )
                                        :attr( "aria-hidden", "true" )
        local tr  = mw.html.create( "tr" )
                                        :wikitext( boole.show )
        feat()
                         if boole.css then
        if Data.order  and #Data.order > 1 then
                            v:css( boole.css )
            tbl:addClass( "sortable" )
                         end
        end
                        dd:node( v )
--      if Config.classTable then
                    end
--          tbl:addClass( Config.classTable )
                    if type( boole.suffix ) == "string" then
--      end
                         dd:wikitext( boole.suffix )
        if Config.cssTable then
                    end
            if type( Config.cssTable ) == "table" then
                    if boole.lead == false then
                tbl:css( Config.cssTable )
                         dd:wikitext( " " )
            elseif type( Config.cssTable ) == "string" then
                          :node( mw.html.create( "code" )
                -- deprecated
                                        :wikitext( show ) )
                tbl:cssText( Config.cssTable )
                    end
            end
                 elseif s == "suggestedvalues" then
        end
                     local html, values = feasible( param, access )
        tr:node( mw.html.create( "th" )
                     dd:newline()
                        :attr( "colspan", "2" )
                      :node( html )
                        :css( Permit.css.tablehead )
                     Data.params[ access ].suggestedvalues = values
                        :wikitext( factory( "doc-param-name" ) ) )
                 else
          :node( mw.html.create( "th" )
                    dd:wikitext( show )
                        :css( Permit.css.tablehead )
                end
                        :wikitext( factory( "doc-param-desc" ) ) )
                dl:node( mw.html.create( "dt" )
          :node( mw.html.create( "th" )
                                :wikitext( section ) )
                         :css( Permit.css.tablehead )
                  :node( dd )
                         :wikitext( factory( "doc-param-type" ) ) )
            end
          :node( mw.html.create( "th" )
        end -- i = 1, #details
                         :css( Permit.css.tablehead )
        desc:node( dl )
                         :wikitext( factory( "doc-param-status" ) ) )
    end
        tbl:newline()
 
--        :node( mw.html.create( "thead" )
    -- type
                        :node( tr )
    if type( param.type ) == "string" then
--              )
        param.type = mw.text.trim( param.type )
          :newline()
        if param.type == "" then
        if Data.order then
             param.type = false
            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
         end
         if Config.cssTabWrap or Data.scroll then
    end
            r = mw.html.create( "div" )
    if param.type then
             if type( Config.cssTabWrap ) == "table" then
         s    = Permit.types[ param.type ]
                 r:css( Config.cssTabWrap )
        typed = mw.html.create( "td" )
            elseif type( Config.cssTabWrap ) == "string" then
                  :addClass( styles .. "type" )
                 -- deprecated
        if s then
                 r:cssText( Config.cssTabWrap )
             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
             end
             if Data.scroll then
        else
                r:css( "height",  Data.scroll )
             Data.params[ access ].type = "unknown"
                 :css( "overflow", "auto" )
            typed:addClass( "error" )
             end
                 :wikitext( "INVALID" )
            r:node( tbl )
            s = string.format( "params.<code>%s</code>.type", access )
         else
            Fault(  factory( "invalid-value" ):gsub( "$1", s )  )
             r = tbl
             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
     end
     end
     return r
     -- status
end -- features()
     if param.required then
 
         mode = 1
 
         if param.autovalue then
 
            Fault( string.format( "autovalued <code>%s</code> required",
local function finalize( advance )
                                  access ) )
    -- Wrap presentation into frame
             legal = false
    -- 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
         end
         if live then
         if param.default then
             r = r .. fancy( advance, lapsus )
             Fault( string.format( "Defaulted <code>%s</code> required",
                                  access ) )
            legal = false
         end
         end
    end
        if param.deprecated then
    return r
            Fault( string.format( "Required deprecated <code>%s</code>",
end -- finalize()
                                  access ) )
 
            legal = false
 
 
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
    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
     end
     return r
     return r
end -- find()
end -- feature()






local function flat( adjust )
local function features()
     -- Remove formatting from text string
     -- Create <table> for parameters
    -- Parameter:
     -- Returns <table>, or nil
    --    arglist  -- string, to be stripped, or nil
     -- Returns string, or nil
     local r
     local r
     if adjust then
     if Data.tree and Data.tree.params then
         r = adjust:gsub( "\n", " " )
         local tbl = mw.html.create( "table" )
         if r:find( "<noexport>", 1, true ) then
        local tr  = mw.html.create( "tr" )
             r = r:gsub( "<noexport>(.*)</noexport>", "" )
        feat()
         if Data.order  and  #Data.order > 1 then
             tbl:addClass( "sortable" )
         end
         end
         if r:find( "''", 1, true ) then
         if type( Config.classTable ) == "table" then
             r = r:gsub( "'''", "" ):gsub( "''", "" )
             for k, v in pairs( Config.classTable ) do
                tbl:addClass( v )
            end -- for k, v
         end
         end
         if r:find( "<", 1, true ) then
         if type( Config.cssTable ) == "table" then
             local Text = Fetch( "Text" )
            tbl:css( Config.cssTable )
             r = Text.getPlain( r:gsub( "<br */?>", "\r\n" ) )
        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
        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 )
        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
     end
    r = r .. "\n}\n}"
     return r
     return r
end -- flush()
end -- features()






local function focus( access )
local function fellow( any, assigned, at )
     -- Check components; focus multilingual description, build trees
     -- Check sets[] parameter and issue error message, if necessary
     -- Parameter:
     -- Parameter:
     --    access -- string, name of parameter, nil for root
     --    any      -- should be number
     local f = function ( a, at )
    --    assigned -- parameter name
                    local r
    --    at        -- number, of set
                     if at then
    local s
                         r = string.format( "<code>params.%s</code>", at )
    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
                     else
                         r = "''root''"
                         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
                     end
                     if a then
                else
                        r = string.format( "%s<code>.%s</code>", r, a )
                     k = mw.text.nowiki( tostring( k ) )
                     end
                    s = string.format( "<code>sets[%s]</code> ?????", k )
                    return r
                     Fault( s )
                 end
                 end
    local parent
            end -- for k, v
    if access then
        else
        parent = Data.got.params[ access ]
            s = "<code>params</code> required for <code>sets</code>"
            Fault( s )
        end
     else
     else
         parent = Data.got
         s = "<code>sets</code> needs to be of <code>object</code> type"
        Fault( s )
     end
     end
     if type( parent ) == "table" then
end -- fellows()
         local elem, got, permit, s, scope, slot, tag, target
 
         if access then
 
             permit = Permit.params
 
             if type( access ) == "number" then
local function finalize( advance )
                slot = tostring( access )
    -- Wrap presentation into frame
            else
    -- Parameter:
                slot = access
    --    advance  -- true, for nice
            end
    -- Returns string
         else
    local r, lapsus
             permit = Permit.root
     if Data.div then
         end
        r = tostring( Data.div )
        for k, v in pairs( parent ) do
    elseif Data.strip then
            scope = permit[ k ]
        r = Data.strip
            if scope then
    else
                s = type( v )
        lapsus = true
                if s == "string"  and  k ~= "format" then
        r      = ""
                    v = mw.text.trim( v )
    end
                end
    r = r .. failures()
                if scope:find( s, 1, true ) then
    if Data.source then
                    if scope:find( "I18N", 1, true ) then
         local live = ( advance or lapsus )
                        if s == "string" then
         if not live then
                            elem = fair( v )
             live = TemplateData.frame:preprocess( "{{REVISIONID}}" )
                        elseif s == "table" then
             live = ( live == "" )
                            local translated
        end
                            v, translated = faraway( v )
         if live then
                            if v then
             r = r .. fancy( advance, lapsus )
                                if translated  and
         end
                                  k == "description" then
    end
                                    elem = { [ 1 ] = fair( v ),
    return r
                                            [ 2 ] = translated }
end -- finalize()
                                else
 
                                    elem = fair( v )
 
                                end
 
                            else
local function find()
                                elem = false
    -- Find JSON data within page source (title)
                            end
    -- Returns string, or nil
                        end
    local s = Data.title:getContent()
                        if type( v ) == "string" then
    local i, j = s:find( "<templatedata>", 1, true )
                            if k == "deprecated" then
    local r
                                if v == "1" then
    if i then
                                    v = true
        local k = s:find( "</templatedata>", j, true )
                                elseif v == "0" then
        if k then
                                    v = false
          r = mw.text.trim( s:sub( j + 1, k - 1 ) )
                                end
        end
                                elem = v
    end
                            elseif scope:find( "nowiki", 1, true ) then
    return r
                                elem = mw.text.nowiki( v )
end -- find()
                                elem = elem:gsub( "&#13;\n", "<br>" )
 
                                v    = v:gsub( string.char( 13 ), "" )
 
                            else
 
                                v = flat( v )
local function flat( adjust )
                            end
    -- Remove formatting from text string for VE
                        elseif s == "boolean" then
    -- Parameter:
                            if scope:find( "boolean", 1, true ) then
    --    arglist  -- string, to be stripped, or nil
                                elem = v
    -- Returns string, or nil
                            else
    local r
                                s = "Type <code>boolean</code> bad for "
    if adjust then
                                    .. f( k, slot )
        r = adjust:gsub( "\n", " " )
                                Fault( s )
        if r:find( "<noexport>", 1, true ) then
                            end
            r = r:gsub( "<noexport>.*</noexport>", "" )
                        end
        end
                    else
        if r:find( "<exportonly>", 1, true ) then
                        if k == "params" and  not access then
            r = r:gsub( "</?exportonly>", "" )
                            v    = nil
        end
                            elem = nil
        if r:find( "''", 1, true ) then
                        elseif k == "format" and  not access then
            r = r:gsub( "'''", "" ):gsub( "''", "" )
                            elem = mw.text.decode( v )
        end
                            v    = nil
        if r:find( "<", 1, true ) then
                        elseif k == "inherits" then
            local Text = Fetch( "Text" )
                            elem = v
            r = Text.getPlain( r:gsub( "<br */?>", "\r\n" ) )
                            if not Data.heirs then
        end
                                Data.heirs = { }
        if r:find( "[", 1, true ) then
                            end
            local WLink = Fetch( "WLink" )
                            Data.heirs[ slot ] = v
            if WLink.isBracketedURL( r ) then
                            v                  = nil
                r = r:gsub( "%[([hf]tt?ps?://%S+) [^%]]+%]", "%1" )
                        elseif k == "style" then
            end
                            elem = v
            r = WLink.getPlain( r )
                            v    = nil
        end
                        elseif s == "string" then
        if r:find( "&", 1, true ) then
                            v    = mw.text.nowiki( v )
            r = mw.text.decode( r )
                            elem = v
            if r:find( "&shy;", 1, true ) then
                        else
                r = r:gsub( "&shy;", "" )
                            elem = v
            end
                        end
        end
                    end
    end
                    if type( elem ) ~= "nil" then
    return r
                        if not target then
end -- flat()
                            if access then
 
                                if not Data.tree.params then
 
                                    Data.tree.params = { }
 
                                 end
local function flush()
                                 Data.tree.params[ slot ] = { }
    -- JSON encode narrowed input; obey unnamed (numerical) parameters
                                 target = Data.tree.params[ slot ]
    -- Returns <templatedata> JSON string
                            else
    local r
                                Data.tree = { }
    if Data.tag then
                                target    = Data.tree
        r = mw.text.jsonEncode( Data.tag ):gsub( "%}$", "," )
                            end
    else
                        end
        r = "{"
                        target[ k ] = elem
    end
                        elem        = false
    r = r .. "\n\"params\":{"
                    end
    if Data.order then
                    if type( v ) ~= "nil" then
        local sep = ""
                        if not tag then
        local s
                            if access then
        for i = 1, #Data.order do
                                if type( v ) == "string"  and
            s  = Data.order[ i ]
                                  v.sub( 1, 1 ) == "=" then
            r  = string.format( "%s%s\n%s:%s",
                                    v = nil
                                r,
                                else
                                 sep,
                                    if not Data.params then
                                 mw.text.jsonEncode( s ),
                                        Data.params = { }
                                 mw.text.jsonEncode( Data.params[ s ] ) )
                                    end
            sep = ",\n"
                                    Data.params[ slot ] = { }
        end -- for i = 1, #Data.order
                                    tag = Data.params[ slot ]
    end
                                end
    r = r .. "\n}\n}"
                            else
    return r
                                Data.tag = { }
end -- flush()
                                tag      = Data.tag
 
                            end
 
                        end
 
                         if type( v ) ~= "nil" then
local function focus( access )
                            tag[ k ] = v
    -- Check components; focus multilingual description, build trees
                        end
    -- 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
                     end
                else
                     return r
                     s = string.format( "Type <code>%s</code> bad for %s",
                                      scope,  f( k, slot ) )
                    Fault( s )
                 end
                 end
            else
    local parent
                Fault( "Unknown component " .. f( k, slot ) )
    if access then
            end
        parent = Data.got.params[ access ]
        end -- for k, v
     else
     else
         Fault( f() .. " needs to be of <code>object</code> type" )
         parent = Data.got
     end
     end
end -- focus()
     if type( parent ) == "table" then
 
         local elem, got, permit, s, scope, slot, tag, target
 
         if access then
 
             permit = Permit.params
local function format()
             if type( access ) == "number" then
     -- Build formatted element
                 slot = tostring( access )
    -- 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
             else
                 s = mw.text.nowiki( source ):gsub( "\n", "&#92;n" )
                 slot = access
                s = tostring( mw.html.create( "code" )
                                    :wikitext( s ) )
                Fault( "Invalid format " .. s )
                source = false
             end
             end
         else
         else
             local words = mw.text.split( source, "%s+" )
             permit = Permit.root
             local show, start, support, unknown
        end
             for i = 1, #words do
        for k, v in pairs( parent ) do
                 s = words[ i ]
             scope = permit[ k ]
                 if i == 1 then
             if scope then
                     start = s
                 s = type( v )
                 if s == "string"  and  k ~= "format" then
                     v = mw.text.trim( v )
                 end
                 end
                 support = Permit.builder[ s ]
                 if scope:find( s, 1, true ) then
                if support == start  or
                    if scope:find( "I18N", 1, true ) then
                  support == "*" then
                        if s == "string" then
                    Permit.builder[ s ] = true
                            elem = fair( v )
                elseif s:match( "^[1-9]%d?" ) and
                        elseif s == "table" then
                      Permit.builder.align then
                            local translated
                    Permit.builder.align = tonumber( s )
                            v, translated = faraway( v )
                else
                            if v then
                    if unknown then
                                if translated  and
                        unknown = string.format( "%s %s", unknown, s )
                                  k == "description" then
                    else
                                    elem = { [ 1 ] = fair( v ),
                        unknown = s
                                            [ 2 ] = translated }
                    end
                                else
                end
                                    elem = fair( v )
            end -- i = 1, #words
                                end
            if unknown then
                            else
                s = tostring( mw.html.create( "code" )
                                elem = false
                                    :css( "white-space", "nowrap" )
                            end
                                    :wikitext( s ) )
                        end
                Fault( "Unknown/misplaced format keyword " .. s )
                        if type( v ) == "string" then
                source = false
                            if k == "deprecated" then
                start  = false
                                if v == "1" then
            end
                                    v = true
            if start == "inline" then
                                elseif v == "0" then
                if Permit.builder.half == true then
                                    v = false
                    show = "inline half"
                                end
                    code = "{{_ |_=_}}"
                                elem = v
                elseif Permit.builder.grouped == true then
                            elseif scope:find( "nowiki", 1, true ) then
                    show = "inline grouped"
                                elem = mw.text.nowiki( v )
                    code = "{{_ | _=_}}"
                                elem = elem:gsub( "&#13;\n", "<br>" )
                elseif Permit.builder.spaced == true then
                                v    = v:gsub( string.char( 13 ),  "" )
                    show = "inline spaced"
                            else
                    code = "{{_ | _ = _ }}"
                                v = flat( v )
                end
                            end
                if Permit.builder.newlines == true then
                        elseif s == "boolean" then
                    show = show or "inline"
                            if scope:find( "boolean", 1, true ) then
                    code = code or "{{_|_=_}}"
                                elem = v
                    show = show .. " newlines"
                            else
                    code = string.format( "N%sN", code )
                                s = "Type <code>boolean</code> bad for "
                end
                                    .. f( k, slot )
            elseif start == "block" then
                                Fault( s )
                local space  = ""     -- amid "|" and name
                            end
                local spaced = " "    -- preceding "="
                         end
                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
                     else
                         suffix = ""
                         if k == "params" and  not access then
                    end
                            v    = nil
                else
                            elem = nil
                    if Permit.builder.lead == true then
                        elseif k == "format"  and  not access then
                        show  = show .. " lead"
                            elem = mw.text.decode( v )
                         space = " "
                            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
                     end
                     if type( Permit.builder.align ) ~= "string" then
                     if type( elem ) ~= "nil" then
                        local n
                         if not target then
                        s = " align"
                             if access then
                         if Permit.builder.align == true then
                                if not Data.tree.params then
                             n = 0
                                    Data.tree.params = { }
                            if type( Data.got ) == "table"  and
                                end
                              type( Data.got.params ) == "table" then
                                 Data.tree.params[ slot ] = { }
                                for k, v in pairs( Data.got.params ) do
                                 target = Data.tree.params[ slot ]
                                    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
                             else
                                 n = 0   -- How comes?
                                 Data.tree = { }
                                target   = Data.tree
                             end
                             end
                         end
                         end
                         if n > 1 then
                        target[ k ] = elem
                             spaced = string.rep( "_", n ) .. " "
                        elem        = false
                        end
                    end
                        show = show .. s
                    if type( v ) ~= "nil" then
                    elseif Permit.builder.after == true then
                         if not tag then
                        spaced = ""
                             if access then
                        show  = show .. " after"
                                if type( v ) == "string"  and
                    elseif Permit.builder.dense == true then
                                  v.sub( 1, 1 ) == "=" then
                         spaced = ""
                                    v = nil
                         spacer = ""
                                else
                        show  = show .. " dense"
                                    if not Data.params then
                    end
                                        Data.params = { }
                    if Permit.builder.last == true then
                                    end
                        suffix = spacer
                                    Data.params[ slot ] = { }
                         show  = show .. " last"
                                    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
                     end
                else
                    s = string.format( "Type <code>%s</code> bad for %s",
                                      scope,  f( k, slot ) )
                    Fault( s )
                 end
                 end
                 code = string.format( "N{{_N%s|%s_%s=%s_%s}}N",
            else
                                      start,
                 Fault( "Unknown component " .. f( k, slot ) )
                                      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
        end -- for k, v
        if not access  and Data.got.sets then
            fellows()
         end
         end
         if code then
    else
            source = code:gsub( "N", "\n" )
         Fault( f() .. " needs to be of <code>object</code> type" )
            code  = mw.text.nowiki( code ):gsub( "N", "&#92;n" )
     end
            code  = mw.html.create( "code" )
end -- focus()
                            :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 then
        Data.tag.format = source
     end
    return r
end -- format()




 
local function format()
local function formatter()
     -- Build formatted element
     -- Build presented documentation
     -- Returns <inline>
     -- Returns <div>
     local source = Data.tree.format:lower()
     local r = mw.html.create( "div" )
     local r, s
     local x = fashioned( Data.tree, true, r )
     if source == "inline"  or  source == "block" then
    local s
         r = mw.html.create( "i" )
     if x then
                  :wikitext( source )
         r = x
    else
    end
         local code
    if Data.leading then
         if source:find( "|", 1, true ) then
        local toc = mw.html.create( "div" )
             local scan = "^[\n ]*%{%{[\n _]*|[\n _]*=[\n _]*%}%}[\n ]*$"
         local shift
             if source:match( scan ) then
         if Config.suppressTOCnum then
                code = source:gsub( "\n", "N" )
             toc:addClass( Config.suppressTOCnum )
            else
             if type( Config.stylesTOCnum ) == "string" then
                 s = mw.text.nowiki( source ):gsub( "\n", "&#92;n" )
                 local src = Config.stylesTOCnum .. "/styles.css"
                 s = tostring( mw.html.create( "code" )
                 s = TemplateData.frame:extensionTag( "templatestyles",
                                    :wikitext( s ) )
                                                    nil,
                Fault( "Invalid format " .. s )
                                                    { src = src } )
                source = false
                r:newline()
                :node( s )
             end
             end
         end
         else
        toc:css( "margin-top", "0.5em" )
            local words = mw.text.split( source, "%s+" )
          :wikitext( "__TOC__" )
            local show, start, support, unknown
        if Data.sibling then
            for i = 1, #words do
            local block = mw.html.create( "div" )
                s = words[ i ]
            if TemplateData.ltr then
                if i == 1 then
                shift = "right"
                    start = s
            else
                end
                 shift = "left"
                support = Permit.builder[ s ]
             end
                if support == start  or
             block:css( "float", shift )
                  support == "*" then
                :wikitext( Data.sibling )
                    Permit.builder[ s ] = true
            r:newline()
                elseif s:match( "^[1-9]%d?" ) and
            :node( block )
                      Permit.builder.align then
            :newline()
                    Permit.builder.align = tonumber( s )
        end
                else
        r:newline()
                    if unknown then
        :node( toc )
                        unknown = string.format( "%s %s", unknown, s )
        :newline()
                    else
        if shift then
                        unknown = s
            r:node( mw.html.create( "div" )
                    end
                          :css( "clear", shift ) )
                 end
            :newline()
             end -- i = 1, #words
        end
             if unknown then
    end
                s = tostring( mw.html.create( "code" )
    s = features()
                                    :css( "white-space", "nowrap" )
    if s then
                                    :wikitext( s ) )
        if Data.leading then
                Fault( "Unknown/misplaced format keyword " .. s )
            r:node( mw.html.create( "h2" )
                source = false
                          :wikitext( factory( "doc-params" ) ) )
                start  = false
            :newline()
            end
        end
            if start == "inline" then
        r:node( s )
                if Permit.builder.half == true then
    end
                    show = "inline half"
    if Data.shared then
                    code = "{{_ |_=_}}"
        local global = mw.html.create( "div" )
                elseif Permit.builder.grouped == true then
                              :attr( "id", "templatedata-global" )
                    show = "inline grouped"
        local shift
                    code = "{{_ | _=_}}"
        if TemplateData.ltr then
                elseif Permit.builder.spaced == true then
            shift = "right"
                    show = "inline spaced"
        else
                    code = "{{_ | _ = _ }}"
            shift = "left"
                end
        end
                if Permit.builder.newlines == true then
        global:css( "float", shift )
                    show = show or "inline"
              :wikitext( string.format( "[[%s|%s]]",
                    code = code or "{{_|_=_}}"
                                        Data.shared, "Global" ) )
                    show = show .. " newlines"
        r:newline()
                    code = string.format( "N%sN", code )
        :node( global )
                end
    end
            elseif start == "block" then
    if Data.tree and Data.tree.format then
                local space  = ""    -- amid "|" and name
        local e = format()
                local spaced = " "    -- preceding "="
        if e then
                local spacer = " "    -- following "="
             local show = "Format"
                local suffix = "N"    -- closing "}}" on new line
             if Config.supportFormat then
                show = "block"
                 show = string.format( "[[%s|%s]]",
                if Permit.builder.indent == true then
                                      Config.supportFormat, show )
                    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
            r:node( mw.html.create( "p" )
                          :wikitext( show .. ": " )
                          :node( e ) )
        end
    end
    return r
end -- formatter()
local function free()
    -- Remove JSON comment lines
    Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([},\"'])",
                      "%1%3" )
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
    end -- for k, v
    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
     end
     if arglist.lang then
     if source and Data.tag then
         Data.slang = arglist.lang:lower()
         Data.tag.format = source
    elseif adapt.lang then
        Data.slang = adapt.lang:lower()
     end
     end
     if arglist.JSON then
     return r
        source = arglist.JSON
end -- format()
    elseif arglist.Global then
 
        source = TemplateData.getGlobalJSON( arglist.Global,
 
                                            arglist.Local )
 
     elseif arglist[ 1 ] then
local function formatter()
        local s    = mw.text.trim( arglist[ 1 ] )
    -- Build presented documentation
        local start = s:sub( 1, 1 )
     -- Returns <div>
        if start == "<" then
    local r = mw.html.create( "div" )
            Data.strip = s
    local x = fashioned( Data.tree, true, r )
        elseif start == "{" then
    local s
            source = s
    if x then
         elseif mw.ustring.sub( s, 1, 8 ) ==
         r = x
              mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then
            Data.strip = s
        end
     end
     end
     if type( arglist.vertical ) == "string"  and
     if Data.leading then
      arglist.vertical:match( "^%d*%.?%d+[emprx]+$" ) then
         local toc = mw.html.create( "div" )
        Data.scroll = arglist.vertical
         local shift
    end
         if Config.suppressTOCnum then
    if not source then
            toc:addClass( Config.suppressTOCnum )
         Data.title = mw.title.getCurrentTitle()
            if type( Config.stylesTOCnum ) == "string" then
         source = find()
                local src = Config.stylesTOCnum .. "/styles.css"
         if not source  and
                s = TemplateData.frame:extensionTag( "templatestyles",
          not Data.title.text:match( Config.subpage ) then
                                                    nil,
            local s = string.format( Config.suffix,
                                                    { src = src } )
                                    Data.title.prefixedText )
                r:newline()
            Data.title = mw.title.new( s )
                :node( s )
            if Data.title.exists then
                source = find()
             end
             end
         end
         end
    end
         toc:css( "margin-top", "0.5em" )
    if not Data.lazy then
          :wikitext( "__TOC__" )
        if not Data.title then
         if Data.sibling then
            Data.title = mw.title.getCurrentTitle()
             local block = mw.html.create( "div" )
        end
             if TemplateData.ltr then
         Data.lazy = Data.title.text:match( Config.subpage )
                 shift = "right"
    end
            else
    if type( source ) == "string" then
                 shift = "left"
        TemplateData.getPlainJSON( source )
    end
    return finalize( faculty( arglist.source ) )
end -- furnish()
 
 
 
TemplateData.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --    atleast  -- string, with required version or "wikidata"
    --                or false
    -- Postcondition:
    --    Returns  string with appropriate version, or false
    local since = atleast
    local r
    if since == "wikidata" then
         local item = TemplateData.item
        since = false
        if type( item ) == "number"  and  item > 0 then
             local entity = mw.wikibase.getEntity( string.format( "Q%d",
                                                                item ) )
             if type( entity ) == "table" then
                 local vsn = entity:formatPropertyValues( "P348" )
                 if type( vsn ) == "table"  and
                  type( vsn.value ) == "string" and
                  vsn.value ~= "" then
                    r = vsn.value
                end
             end
             end
            block:css( "float", shift )
                :wikitext( Data.sibling )
            r:newline()
            :node( block )
            :newline()
         end
         end
     end
        r:newline()
     if not r then
        :node( toc )
         if not since  or  since <= TemplateData.serial then
        :newline()
             r = TemplateData.serial
        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
         else
             r = false
             shift = "left"
         end
         end
        global:css( "float", shift )
              :wikitext( string.format( "[[%s|%s]]",
                                        Data.shared, "Global" ) )
        r:newline()
        :node( global )
     end
     end
     return r
    if Data.tree and Data.tree.format then
end -- TemplateData.failsafe()
        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()






TemplateData.getGlobalJSON = function ( access, adapt )
local function free()
     -- Retrieve TemplateData from a global repository (JSON)
    -- Remove JSON comment lines
     -- Parameter:
    if Data.source:find( "//", 1, true ) then
     --    access  -- string, with page specifier (on WikiMedia Commons)
        Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([{},\"'])",
     --    adapt  -- JSON string or table with local overrides
                          "%1%3" )
     -- Returns true, if succeeded
    end
     local plugin = Fetch( "/global" )
end -- free()
     local r
 
     if type( plugin ) == "table"  and
 
       type( plugin.fetch ) == "function" then
 
         local s, got = plugin.fetch( access, adapt )
local function full()
         if got then
    -- Build survey table from JSON data, append invisible <templatedata>
             Data.got    = got
    Data.div = mw.html.create( "div" )
             Data.order  = got.paramOrder
                      :addClass( "mw-templatedata-doc-wrap" )
             Data.shared = s
    focus()
             r          = true
    if Data.tag then
             full()
        if type( Data.got.params ) == "table" then
         else
            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 )
             Fault( s )
         end
         end
     end
     end
     return r
     return r
end -- TemplateData.getGlobalJSON()
end -- TemplateData.getGlobalJSON()
 
 
 
 
 
 
TemplateData.getPlainJSON = function ( adapt )
TemplateData.getPlainJSON = function ( adapt )
     -- Reduce enhanced JSON data to plain text localized JSON
     -- Reduce enhanced JSON data to plain text localized JSON
     -- Parameter:
     -- Parameter:
     --    adapt  -- string, with enhanced JSON
     --    adapt  -- string, with enhanced JSON
     -- Returns string, or not
     -- Returns string, or not
     if type( adapt ) == "string" then
     if type( adapt ) == "string" then
         local JSONutil = Fetch( "JSONutil", true )
         local JSONutil = Fetch( "JSONutil", true )
         Data.source = adapt
         Data.source = adapt
         free()
         free()
         if JSONutil then
         if JSONutil then
             local Multilingual = Fetch( "Multilingual", true )
             local Multilingual = Fetch( "Multilingual", true )
             local f
             local f
             if Multilingual then
             if Multilingual then
                 f = Multilingual.i18n
                 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
             end
            Data.got = JSONutil.fetch( Data.source, true, f )
             Fault( "fatal JSON error: " .. scream )
        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
             Fault( "fatal JSON error: " .. Data.got )
         end
         end
     end
     end
Zeile 1.812: Zeile 2.242:
     end
     end
     return r
     return r
end -- p.f()
end -- p.f


p.failsafe = function ( frame )
p.failsafe = function ( frame )
Zeile 1.829: Zeile 2.259:
         end
         end
     end
     end
     return TemplateData.failsafe( since )  or  ""
     return Failsafe.failsafe( since )  or  ""
end -- p.failsafe()
end -- p.failsafe


p.TemplateData = function ()
p.TemplateData = function ()

Aktuelle Version vom 27. Januar 2023, 13:51 Uhr

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}}


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