Module:Format TemplateData: Difference between revisions

m (1 revision imported)
(Import from de:Modul:TemplateData)
Line 1: Line 1:
local TemplateData = { serial = "2017-11-06",
local TemplateData = { suite  = "TemplateData",
                       suite  = "TemplateData" }
                      serial = "2022-03-10",
local plaintext = require("Module:Plain text")
                       item  = 46997995 }
--[=[
--[==[
improve template:TemplateData
improve template:TemplateData
]=]
]==]
local Failsafe = TemplateData
 
 
local Config = {
local Config = {
     -- multiple #invoke option names mapped into unique internal fields
     -- multiple option names mapped into unique internal fields
     cat            = "strange",
     basicCnf = { catProblem          = "strange",
    classNoNumTOC = "suppressTOCnum",
                classMultiColumns  = "selMultClm",
-- classParams    = "classTable",
                classNoNumTOC       = "suppressTOCnum",
    cssParams      = "stylesTable",
                classTable          = "classTable",
    cssParWrap    = "stylesTabWrap",
                cssParWrap          = "cssTabWrap",
    debug         = false,
                cssParams          = "cssTable",
    docpageCreate  = "suffix",
                docpageCreate      = "suffix",
    docpageDetect  = "subpage",
                docpageDetect      = "subpage",
    msgDescMiss   = "solo",
                helpBoolean        = "support4boolean",
--  classTable    = false,    -- class for params table
                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",
                tStylesMultiColumns = "stylesMultClm" },
    classTable    = { "wikitable" },    -- classes for params table
    debugmultilang = "C0C0C0",
     loudly        = false,    -- show exported element, etc.
     loudly        = false,    -- show exported element, etc.
     solo          = false,    -- complaint on missing description
     solo          = false,    -- complaint on missing description
     strange        = false,    -- title of maintenance category
     strange        = false,    -- title of maintenance category
     stylesTable    = false,    -- styles for params table
     cssTable      = false,    -- styles for params table
     stylesTabWrap  = false,    -- styles for params table wrapper
     cssTabWrap    = false,    -- styles for params table wrapper
    debug          = false,
     subpage        = false,    -- pattern to identify subpage
     subpage        = false,    -- pattern to identify subpage
     suffix        = false,     -- subpage creation scheme
     suffix        = false,   -- subpage creation scheme
     suppressTOCnum = false    -- class for TOC number suppression
     suppressTOCnum = false,   -- class for TOC number suppression
    jsonDebug      = "json-code-lint"    -- class for jsonDebug tool
}
}
local Data = {
local Data = {
Line 30: Line 50:
     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
Line 38: Line 59:
     params  = false,    -- table, exported parameters
     params  = false,    -- table, exported parameters
     scream  = false,    -- error messages
     scream  = false,    -- error messages
     slang  = false,   -- project language code
    sibling = false,    -- TOC juxtaposed
     slang  = nil,     -- project/user language code
     slim    = false,    -- JSON reduced to plain
     slim    = false,    -- JSON reduced to plain
     source  = false,    -- JSON input
     source  = false,    -- JSON input
Line 47: Line 69:
}
}
local Permit = {
local Permit = {
     styles = { required    = "border-left: 3px solid black;",
     builder = { after          = "block",
                 suggested  = "border-left: 3px solid #888;",
                align          = "block",
                 optional    = "border-left: 3px solid #ccc",
                block          = "block",
                 deprecated  = "border-left: 3px dotted red; background-color: #FDD;",
                compressed      = "block",
                tableheadbg = "background-color: #B3B7FF;" },
                dense          = "block",
     params  = { aliases     = "table",
                grouped        = "inline",
                 autovalue   = "string",
                half            = "inline",
                 default     = "string table I18N nowiki",
                indent          = "block",
                 deprecated = "boolean string",
                inline          = "inline",
                 description = "string table I18N",
                last            = "block",
                 example     = "string table I18N nowiki",
                lead            = "block",
                 label       = "string table I18N",
                newlines        = "*",
                 inherits   = "string",
                spaced          = "inline" },
                 required   = "boolean",
    colors = { bg          = "FFFFFF",
                 suggested   = "boolean",
                fg          = "000000",
                 suggestedvalues = "table",
                tableheadbg = "B3B7FF",
                 type       = "string" },
                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 boolean",
                 type           = "string" },
     root    = { description = "string table I18N",
     root    = { description = "string table I18N",
                 format      = "string",
                 format      = "string",
Line 89: Line 127:
}
}


--
-- Generic utility functions
--


local function _ne( value )
-- Is string not empty?
-- Parameter:
--    value  -- the value to test
-- Return:
--    boolean -- whether `value` is truthy and not the empty string
return value and value ~= ''
end -- _ne


local function Fault( alert )
local function Fault( alert )
Line 113: Line 140:
end -- Fault()
end -- Fault()


local function collapseWhitespace ( a )
-- Collapses whitespace, HTML style.
    return a:gsub( "%s*\n%s*", " " )
            :gsub( "%s%s+", " " )
end -- collapseWhitespace


--------------------------------------------------------------------------------
 
--
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 )
local function facet( ask, at )
Line 132: Line 217:
                                   :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 )
Line 144: Line 237:
             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
Line 152: Line 245:




local function getLocalizedText( adapt )
local function facilities( apply )
    -- Retrieve details of suggestedvalues
    -- Parameter:
    --    apply  -- table, with plain or enhanced values
    --              .suggestedvalues  -- table|string|number, or more
    -- Returns
    --    1  -- table, with suggestedvalues
    --    2  -- table, with CSS map, or not
    --    3  -- string, with class, or not
    --    4  -- string, with templatestyles, or not
    local elements = apply.suggestedvalues
    local s        = type( elements )
    local r1, r2, r3, r4
    if s == "table" then
        local values = elements.values
        if type( values ) == "table" then
            r1 = values
            if type( elements.scroll ) == "string" then
                r2 = r2  or  { }
                r2.height  = apply.scroll
                r2.overflow = "auto"
            end
            if type( elements.minwidth ) == "string" then
                local s = type( elements.maxcolumns )
                r2 = r2  or  { }
                r2["column-width"] = elements.minwidth
                if s == "string"  or
                  s == "number" then
                    s = tostring( elements.maxcolumns )
                    r2["column-count"] = s
                end
                if type( Config.selMultClm ) == "string" then
                    r3 = Config.selMultClm
                end
                if type( Config.stylesMultClm ) == "string" then
                    local src = Config.stylesMultClm .. "/styles.css"
                    r4 = TemplateData.frame
                                    :extensionTag( "templatestyles",
                                                    nil,
                                                    { src = src } )
                end
            end
        elseif elements  and  elements ~= "" then
            r1 = elements
        end
    elseif s == "string" then
        s = mw.text.trim( about )
        if s ~= "" then
            r1 = { }
            table.insert( r1,
                          { code = s } )
        end
    elseif s == "number" then
        r1 = { }
        table.insert( r1,
                      { code = tostring( elements ) } )
    end
    return r1, r2, r3, r4
end -- facilities()
 
 
 
local function factory( adapt )
     -- Retrieve localized text from system message
     -- Retrieve localized text from system message
     -- Parameter:
     -- Parameter:
     --    adapt  -- string, message ID after "templatedata-"
     --    adapt  -- string, message ID after "templatedata-"
     -- Returns string, with localized text
     -- Returns string, with localized text
     return mw.message.new( "templatedata-" .. adapt ):plain()
     local o = mw.message.new( "templatedata-" .. adapt )
end -- getLocalizedText()
    if Foreign() then
        o:inLanguage( Data.slang )
    end
    return o:plain()
end -- factory()




Line 204: Line 363:




local function handleNoexportWhitespace( adjust )
local function fair( adjust )
     -- Reduces runs of spaces, including newlines, to a single space, so the
     -- Reduce text to one line of plain text, or noexport wikitext blocks
    -- whole string is on one line. <noexport> blocks are left alone, but the
    -- <noexport> tags themselves are removed.
     --    adjust  -- string
     --    adjust  -- string
     -- Returns string, with adjusted text
     -- 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
     local r
     if adjust:find( "<noexport>", 1, true ) then
     if Data.source then
         local i    = 1
         local support = Config.jsonDebug
         local j, k = adjust:find( "<noexport>", i, true )
         local css
        r = ""
        if advance then
        while j do
            css = { height = "6em",
             if j > 1 then
                    resize = "vertical" }
                 r = r .. collapseWhitespace( adjust:sub( i,  j - 1 ) )
            r  = { [ 1 ] = "syntaxhighlight",
                    [ 2 ] = Data.source,
                    lang  = "json",
                    style = table.concat( css, ";" ) }
             if alert then
                 r.class( support )
             end
             end
             i = k + 1
             r = TemplateData.frame:callParserFunction( "#tag", r )
            j, k = adjust:find( "</noexport>", i, true )
        else
             if j then
             css = { [ "font-size" ]  = "77%",
                r    = r .. adjust:sub( i, j - 1 )
                    [ "line-height" ] = "1.35" }
                i    = k + 1
            if alert then
                 j, k = adjust:find( "<noexport>", i, true )
                 css.resize = "vertical"
             else
             else
                 Fault( "missing </noexport>" )
                 css.display = "none"
             end
             end
         end   -- while j
            r = mw.html.create( "pre" )
         r = r .. adjust:sub( i )
                      :addClass( support )
                      :css( css )
                      :wikitext( mw.text.encode( Data.source ) )
            r = tostring( r )
         end
         r = "\n".. r
     else
     else
         r = collapseWhitespace( adjust )
         r = ""
     end
     end
     return r
     return r
end -- handleNoexportWhitespace()
end -- fancy()






local function faraway( alternatives )
local function faraway( alternatives )
     -- Retrieve project language version from multilingual text
     -- Retrieve best language version from multilingual text
     -- Parameter:
     -- Parameter:
     --    alternatives  -- table, to be evaluated
     --    alternatives  -- table, to be evaluated
Line 248: Line 470:
     local variants = { }
     local variants = { }
     local r1, r2
     local r1, r2
    if not Data.slang then
        Data.slang = mw.language.getContentLanguage():getCode()
    end
     for k, v in pairs( alternatives ) do
     for k, v in pairs( alternatives ) do
         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()
                 variants[ k ] = v
                 variants[ k ] = v
                 n            = n + 1
                 n            = n + 1
Line 261: Line 481:
     end -- for k, v
     end -- for k, v
     if n > 0 then
     if n > 0 then
         for k, v in pairs( variants ) do
         local Multilingual = Fetch( "Multilingual", true )
             if v then
        if Multilingual  and
                 if n == 1 then
          type( Multilingual.i18n ) == "function" then
                    r1 = v
            local show, slang = Multilingual.i18n( variants )
                 elseif k:lower() == Data.slang then
             if show then
                    variants[ k ] = nil
                 r1 = show
                    r1 = v
                 variants[ slang ] = nil
                    r2 = variants
                r2 = variants
                    break -- for k, v
                end
             end
             end
         end -- for k, v
         end
         if not r1 then
         if not r1 then
             local seek = string.format( "^%s-", Data.slang )
             Foreign()
             for k, v in pairs( variants ) do
             for k, v in pairs( variants ) do
                 if v and k:lower():match( seek ) then
                 if n == 1 then
                    r1 = v
                elseif Data.slang == k then
                     variants[ k ] = nil
                     variants[ k ] = nil
                     r1 = v
                     r1 = v
                     r2 = variants
                     r2 = variants
                    break -- for k, v
                 end
                 end
             end -- for k, v
             end -- for k, v
            if not r1 then
                local others = mw.language.getFallbacksFor( slang )
                table.insert( others, "en" )
                for i = 1, #others do
                    seek = others[ i ]
                    if variants[ seek ] then
                        r1                  = variants[ seek ]
                        variants[ seek ] = nil
                        r2                  = variants
                        break    -- for i
                    end
                end -- i = 1, #others
            end
            if not r1 then
                for k, v in pairs( variants ) do
                    if v then
                        variants[ k ] = nil
                        r1 = v
                        r2 = variants
                        break -- for k, v
                    end
                end -- for k, v
            end
         end
         end
         if r2 then
         if r2 and Multilingual then
             for k, v in pairs( r2 ) do
             for k, v in pairs( r2 ) do
                 if v then
                 if v and  not Multilingual.isLang( k, true ) then
                local baseCode = k:match( "^%s*(%a%a%a?)-?%a*%s*$" )
                    Fault( string.format( "%s <code>lang=%s</code>",
                if not baseCode or not mw.language.isKnownLanguageTag( baseCode ) then
                                          "Invalid",
                    Fault( string.format( "Invalid <code>lang=%s</code>", k ))
                                          k ) )
                    end
                 end
                 end
             end -- for k, v
             end -- for k, v
Line 320: Line 515:
     return r1, r2
     return r1, r2
end -- faraway()
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()




Line 333: Line 616:
     end -- for k, v
     end -- for k, v
     for i = 1, n do
     for i = 1, n do
         for k, v in pairs( Data.heirs ) do
         if Data.heirs then
            if v  and  not Data.heirs[ v ] then
            for k, v in pairs( Data.heirs ) do
                n              = n - 1
                if v  and  not Data.heirs[ v ] then
                t[ k ].inherits = nil
                    n              = n - 1
                Data.heirs[ k ] = nil
                    t[ k ].inherits = nil
                p2              = { }
                    Data.heirs[ k ] = nil
                t2              = { }
                    p2              = { }
                for k2, v2 in pairs( p[ v ] ) do
                    t2              = { }
                     p2[ k2 ] = v2
                     if p[ v ] then
                end -- for k2, v2
                        for k2, v2 in pairs( p[ v ] ) do
                if p[ k ] then
                    for k2, v2 in pairs( p[ k ] ) do
                        if type( v2 ) ~= "nil" then
                             p2[ k2 ] = v2
                             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
                         end
                    end -- for k2, v2
                        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
                p[ k ] = p2
            end -- for k, v
                for k2, v2 in pairs( t[ v ] ) do
         end
                    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
            end
         end -- for k, v
     end -- i = 1, n
     end -- i = 1, n
     if n > 0 then
     if n > 0 then
Line 380: Line 669:




local function feasible( about, asked )
local function favorize()
     -- Create description head
     -- Local customization issues
     -- Parameter:
     local boole  = { ["font-size"] = "125%" }
     --     about  -- table, supposed to contain description
    local l, cx = pcall( mw.loadData,
     --     asked -- true, if mandatory description
                        TemplateData.frame:getTitle() .. "/config" )
    -- Returns <block>, with head, or nil
    local scripting, style
    local para = mw.html.create( "div" )
    TemplateData.ltr = not mw.language.getContentLanguage():isRTL()
     local plus, r
     if TemplateData.ltr then
    if about and about.description then
        scripting = "left"
         if type( about.description ) == "string" then
    else
             para:wikitext( about.description )
        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
         if k == "fg" then
             style = "color"
         else
         else
             para:wikitext( about.description[ 1 ] )
             style = "background-color"
            plus = mw.html.create( "ul" )
        end
             if not Config.loudly then
        Permit.css[ k ] = { }
                 plus:addClass( "templatedata-maintain" )
        Permit.css[ k ][ style ] = "#" .. v
                     :css( "display", "none" )
    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( about.description[ 2 ] ) do
                plus:node( mw.html.create( "li" )
                                  :node( mw.html.create( "code" )
                                                :wikitext( k ) )
                                  :node( mw.html.create( "br" ) )
                                  :wikitext( handleNoexportWhitespace( v ) ) )
            end -- for k, v
         end
         end
    elseif Config.solo and asked then
        for k, v in pairs( Config.basicCnf ) do
        para:addClass( "error" )
            s = type( cx[ k ] )
            :wikitext( Config.solo )
            if s == "string" or  s == "table" then
        Data.less = true
                Config[ v ] = cx[ k ]
    else
            end
         para = false
         end -- for k, v
     end
     end
     if para then
     if type( Config.subpage ) ~= "string"  or
         if plus then
      type( Config.suffix ) ~= "string" then
            r = mw.html.create( "div" )
         local got = mw.message.new( "templatedata-doc-subpage" )
                      :node( para )
        local suffix
                      :node( plus )
        if got:isDisabled() then
            suffix = "doc"
         else
         else
             r = para
             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
     end
     return r
end -- favorize()
 
 
 
local function feasible( all, at, about )
    -- Deal with suggestedvalues within parameter
    -- Parameter:
    --    all    -- parameter details
    --              .default
    --              .type
    --    at    -- string, with parameter name
    --    about  -- suggestedvalues  -- table,
    --                                  value and possibly description
    --                                  table may have elements:
    --                                    .code    -- mandatory
    --                                    .label  -- table|string
    --                                    .support -- table|string
    --                                    .icon    -- string
    --                                    .class  -- table|string
    --                                    .css    -- table
    --                                    .style  -- string
    --                                    .less    -- true: suppress code
    -- Returns
    --    1: mw.html object <ul>
    --    2: sequence table with values, or nil
    local h = { }
    local e, r1, r2, s, v
    if #about > 0 then
        for i = 1, #about do
            e = about[ 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 )
                if h[ e.code ] then
                    s = string.format( "params.%s.%s REPEATED %s",
                                      at,
                                      "suggestedvalues",
                                      e.code )
                    Fault( s )
                else
                    h[ e.code ] = true
                end
            end
        end -- for i
    else
        Fault( string.format( "params.%s.suggestedvalues %s",
                              at, "NOT AN ARRAY" ) )
    end
    if v then
        local code, d, k, 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 all.type:sub( 1, 5 ) == "wiki-"  and
                  swift == e.code then
                    local rooms = { file = 6,
                                    temp = 10,
                                    user = 2 }
                    local ns = rooms[ all.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
                if e.code == all.default then
                    k = 800
                else
                    k = 300
                end
                code = mw.html.create( "code" )
                              :css( "font-weight", tostring( k ) )
                              :css( "white-space", "nowrap" )
                              :wikitext( swift )
                u:node( code )
            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 all.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  and  v ~= false then
        Fault( string.format( "params.%s.suggestedvalues INVALID", at ) )
        r1 = mw.html.create( "code" )
                    :addClass( "error" )
                    :wikitext( "INVALID" )
    end
     return r1, r2
end -- feasible()
end -- feasible()


Line 445: Line 1,020:
             local pointers = { }
             local pointers = { }
             local points  = { }
             local points  = { }
            local given    = { }
             for k, v in pairs( Data.tree.params ) do
             for k, v in pairs( Data.tree.params ) do
                 i = facet( k, 1 )
                 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
                 if i then
                     table.insert( points, i )
                     table.insert( points, i )
Line 488: Line 1,083:
     local code    = mw.html.create( "code" )
     local code    = mw.html.create( "code" )
     local desc    = mw.html.create( "td" )
     local desc    = mw.html.create( "td" )
    local eager  = mw.html.create( "td" )
     local legal  = true
     local legal  = true
     local param  = Data.tree.params[ access ]
     local param  = Data.tree.params[ access ]
     local ranking = { "required", "suggested", "optional", "deprecated" }
     local ranking = { "required", "suggested", "optional", "deprecated" }
     local r      = mw.html.create( "tr" )
     local r      = mw.html.create( "tr" )
    local styles  = "mw-templatedata-doc-param-"
     local sort, typed
     local sort, typed


Line 520: Line 1,117:
     end
     end
     code = mw.html.create( "td" )
     code = mw.html.create( "td" )
                  :addClass( styles .. "name" )
                   :node( code )
                   :node( code )
     if access:match( "^%d+$" ) then
     if access:match( "^%d+$" ) then
Line 526: Line 1,124:
     end
     end
     if type( param.aliases ) == "table" then
     if type( param.aliases ) == "table" then
         local lapsus
         local lapsus, syn
         for k, v in pairs( param.aliases ) do
         for k, v in pairs( param.aliases ) do
             code:tag( "br" )
             code:tag( "br" )
Line 536: Line 1,134:
                                       :css( "font-style", "italic" )
                                       :css( "font-style", "italic" )
                                       :wikitext( "string" ) )
                                       :wikitext( "string" ) )
                        :wikitext( s )
                else
                    syn = mw.html.create( "span" )
                                :addClass( styles .. "alias" )
                                :css( "white-space", "nowrap" )
                                :wikitext( s )
                    code:node( syn )
                 end
                 end
                code:wikitext( s )
             else
             else
                 lapsus = true
                 lapsus = true
Line 547: Line 1,151:
         if lapsus then
         if lapsus then
             s = string.format( "params.<code>%s</code>.aliases", access )
             s = string.format( "params.<code>%s</code>.aliases", access )
             Fault(  getLocalizedText( "invalid-value" ):gsub( "$1", s )  )
             Fault(  factory( "invalid-value" ):gsub( "$1", s )  )
             legal = false
             legal = false
         end
         end
Line 553: Line 1,157:


     -- description etc.
     -- description etc.
     s = feasible( param )
     s = fashioned( param )
     if s then
     if s then
         desc:node( s )
         desc:node( s )
     end
     end
     if param.suggestedvalues or param.default or param.example or param.autovalue then
    if param.style then
         local details = { "suggestedvalues", "default", "example", "autovalue" }
        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 dl      = mw.html.create( "dl" )
         local dd, section, show, sv
         local dd, section, show
         for i = 1, #details do
         for i = 1, #details do
             s    = details[ i ]
             s    = details[ i ]
             show = param[ s ]
             show = param[ s ]
             if show then
             if show then
                section = getLocalizedText( "doc-param-" .. s )
                dt      = mw.html.create( "dt" ):wikitext( section )
                 dd      = mw.html.create( "dd" )
                 dd      = mw.html.create( "dd" )
                 if type( show ) == "string" and (string.len(show) < 80) then
                 section = factory( "doc-param-" .. s )
                dt:cssText("float: left;margin-right: 1.6em;")
                 if param.type == "boolean"   and
                end
                  ( show == "0" or show == "1" ) then
                 if param.type == "boolean" then
                    local boole = Permit.boole[ ( show == "1" ) ]
                    if (type( show ) == "table") then
                    if boole.lead == true then
                        -- "suggestedvalues"
                        dd:node( mw.html.create( "code" )
                        for i = 1, #show do
                                        :wikitext( show ) )
                            sv = show[ i ]
                          :wikitext( " " )
                            if i > 1 then
                    end
                                dd:wikitext("&#10;")
                    if type( boole.show ) == "string" then
                            end
                        local v = mw.html.create( "span" )
                            if sv == "0" then
                                        :attr( "aria-hidden", "true" )
                                dd:wikitext("<span style=\"color: #610; font-weight: bold;\">False</span> (<code>0</code>)")
                                        :wikitext( boole.show )
                            elseif sv == "1" then
                        if boole.css then
                                dd:wikitext("<span style=\"color: #050; font-weight: bold;\">True</span> (<code>1</code>)")
                             v:css( boole.css )
                            else
                                dd:tag("code"):wikitext( sv )
                             end
                         end
                         end
                     elseif show == "0" then
                        dd:node( v )
                         dd:wikitext("<span style=\"color: #610; font-weight: bold;\">False</span> (<code>0</code>)")
                     end
                     elseif show == "1" then
                    if type( boole.suffix ) == "string" then
                         dd:wikitext("<span style=\"color: #050; font-weight: bold;\">True</span> (<code>1</code>)")
                         dd:wikitext( boole.suffix )
                    else
                     end
                        dd:wikitext( show )
                    if boole.lead == false then
                         dd:wikitext( " " )
                          :node( mw.html.create( "code" )
                                        :wikitext( show ) )
                     end
                     end
                 elseif type( show ) == "table" then
                 elseif s == "suggestedvalues" then
                     -- "suggestedvalues"
                     local v, css, class, ts = facilities( param )
                     for i = 1, #show do
                     if v then
                         sv = show[ i ]
                        local ul
                         if i > 1 then
                         ul, v = feasible( param, access, v )
                             dd:wikitext("&#10;")
                         if v then
                             dd:newline()
                              :node( ul )
                            if css then
                                dd:css( css )
                                if class then
                                    dd:addClass( class )
                                end
                                if ts then
                                    dd:newline()
                                    dd:node( ts )
                                end
                            end
                            Data.params[ access ].suggestedvalues = v
                         end
                         end
                        dd:tag("code"):wikitext( sv )
                     end
                     end
                 else
                 else
                     dd:wikitext( show )
                     dd:wikitext( show )
                 end
                 end
                 dl:node( dt )
                 dl:node( mw.html.create( "dt" )
                                :wikitext( section ) )
                   :node( dd )
                   :node( dd )
             end
             end
Line 614: Line 1,243:


     -- type
     -- 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
     if param.type then
         s    = Permit.types[ param.type ]
         s    = Permit.types[ param.type ]
         typed = mw.html.create( "td" )
         typed = mw.html.create( "td" )
                  :addClass( styles .. "type" )
         if s then
         if s then
             if type( s ) == "string" then
             if s == "string" then
                 Data.params[ access ].type = s
                 Data.params[ access ].type = s
                 typed:wikitext( getLocalizedText( "doc-param-type-" .. s ) )
                 typed:wikitext( factory( "doc-param-type-" .. s ) )
                     :tag( "br" )
                     :tag( "br" )
                 typed:node( mw.html.create( "span" )
                 typed:node( mw.html.create( "span" )
Line 627: Line 1,263:
                 Data.lasting = true
                 Data.lasting = true
             else
             else
                 s = getLocalizedText( "doc-param-type-" .. param.type )
                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 )
                 typed:wikitext( s )
             end
             end
Line 635: Line 1,275:
                 :wikitext( "INVALID" )
                 :wikitext( "INVALID" )
             s = string.format( "params.<code>%s</code>.type", access )
             s = string.format( "params.<code>%s</code>.type", access )
             Fault(  getLocalizedText( "invalid-value" ):gsub( "$1", s )  )
             Fault(  factory( "invalid-value" ):gsub( "$1", s )  )
             legal = false
             legal = false
         end
         end
     else
     else
         typed = mw.html.create( "td" )
         typed = mw.html.create( "td" )
                   :wikitext( getLocalizedText( "doc-param-type-unknown" ) )
                   :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
 
    typed:addClass( "navigation-not-searchable" )
     -- status
     -- status
     if param.required then
     if param.required then
         mode = 1
         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
         if param.deprecated then
             Fault( string.format( "Required deprecated <code>%s</code>",
             Fault( string.format( "Required deprecated <code>%s</code>",
Line 659: Line 1,315:
     end
     end
     status = ranking[ mode ]
     status = ranking[ mode ]
     ranking = getLocalizedText( "doc-param-status-" .. status )
     ranking = factory( "doc-param-status-" .. status )
     if mode == 1  or  mode == 4 then
     if mode == 1  or  mode == 4 then
         ranking = mw.html.create( "span" )
         ranking = mw.html.create( "span" )
Line 667: Line 1,323:
             ranking:tag( "br" )
             ranking:tag( "br" )
             ranking:wikitext( param.deprecated )
             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
     end
     end
    eager:attr( "data-sort-value", tostring( mode ) )
                :node( ranking )
                :addClass( string.format( "%sstatus-%s %s",
                                          styles, status,
                                          "navigation-not-searchable" ) )


     -- <tr>
     -- <tr>
     r:attr( "id",  "templatedata:" .. mw.uri.anchorEncode( access ) )
     r:attr( "id",  "templatedata:" .. mw.uri.anchorEncode( access ) )
     :cssText( Permit.styles[ status ] )
     :css( Permit.css[ status ] )
    :addClass( styles .. status )
     :node( begin )
     :node( begin )
     :node( code )
     :node( code )
     :node( desc )
     :node( desc )
     :node( typed )
     :node( typed )
     :node( mw.html.create( "td" )
     :node( eager )
                  :attr( "data-sort-value", tostring( mode ) )
                  :node( ranking ) )
     :newline()
     :newline()
     if not legal then
     if not legal then
Line 694: Line 1,360:
     local r
     local r
     if Data.tree and Data.tree.params then
     if Data.tree and Data.tree.params then
        local style = Permit.styles.tableheadbg
         local tbl = mw.html.create( "table" )
         local tbl   = mw.html.create( "table" )
         local tr = mw.html.create( "tr" )
                            :addClass( "wikitable" )
         local tr   = mw.html.create( "tr" )
         feat()
         feat()
         if Data.order  and  #Data.order > 1 then
         if Data.order  and  #Data.order > 1 then
             tbl:addClass( "sortable" )
             tbl:addClass( "sortable" )
         end
         end
--      if Config.classTable then
        if type( Config.classTable ) == "table" then
--          tbl:addClass( Config.classTable )
            for k, v in pairs( Config.classTable ) do
--     end
                tbl:addClass( v )
         if Config.stylesTable then
            end -- for k, v
             tbl:cssText( Config.stylesTable )
        end
         if type( Config.cssTable ) == "table" then
             tbl:css( Config.cssTable )
         end
         end
         tr:node( mw.html.create( "th" )
         tr:addClass( "navigation-not-searchable" )
          :node( mw.html.create( "th" )
                         :attr( "colspan", "2" )
                         :attr( "colspan", "2" )
                         :cssText( style )
                         :css( Permit.css.tablehead )
                         :wikitext( getLocalizedText( "doc-param-name" ) ) )
                         :wikitext( factory( "doc-param-name" ) ) )
           :node( mw.html.create( "th" )
           :node( mw.html.create( "th" )
                         :cssText( style )
                         :css( Permit.css.tablehead )
                         :wikitext( getLocalizedText( "doc-param-desc" ) ) )
                         :wikitext( factory( "doc-param-desc" ) ) )
           :node( mw.html.create( "th" )
           :node( mw.html.create( "th" )
                         :cssText( style )
                         :css( Permit.css.tablehead )
                         :wikitext( getLocalizedText( "doc-param-type" ) ) )
                         :wikitext( factory( "doc-param-type" ) ) )
           :node( mw.html.create( "th" )
           :node( mw.html.create( "th" )
                         :cssText( style )
                         :css( Permit.css.tablehead )
                         :wikitext( getLocalizedText( "doc-param-status" ) ) )
                         :wikitext( factory( "doc-param-status" ) ) )
         tbl:newline()
         tbl:newline()
--        :node( mw.html.create( "thead" )
--        :node( mw.html.create( "thead" )
                         :node( tr )
                         :node( tr )
--             )
--             )
           :newline()
           :newline()
         if Data.order then
         if Data.order then
            local leave, s
             for i = 1, #Data.order do
             for i = 1, #Data.order do
                 tbl:node( feature( Data.order[ i ] ) )
                 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
             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.stylesTabWrap then
         if Config.cssTabWrap or Data.scroll then
             r = mw.html.create( "div" )
             r = mw.html.create( "div" )
                  :cssText( Config.stylesTabWrap )
            if type( Config.cssTabWrap ) == "table" then
                  :node( tbl )
                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
         else
             r = tbl
             r = tbl
Line 744: Line 1,439:




local function finalize()
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
     -- Wrap presentation into frame
    -- Parameter:
    --    advance  -- true, for nice
     -- Returns string
     -- Returns string
     local r
     local r, lapsus
     if Data.div then
     if Data.div then
         r = tostring( Data.div )
         r = tostring( Data.div )
Line 753: Line 1,527:
         r = Data.strip
         r = Data.strip
     else
     else
         r = ""
         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
     end
     return r .. failures()
     return r
end -- finalize()
end -- finalize()


Line 778: Line 1,564:


local function flat( adjust )
local function flat( adjust )
     -- Remove formatting from text string
     -- Remove formatting from text string for VE
     -- Parameter:
     -- Parameter:
     --    arglist  -- string, to be stripped, or nil
     --    arglist  -- string, to be stripped, or nil
Line 786: Line 1,572:
         r = adjust:gsub( "\n", " " )
         r = adjust:gsub( "\n", " " )
         if r:find( "<noexport>", 1, true ) then
         if r:find( "<noexport>", 1, true ) then
             r = r:gsub( "<noexport>(.*)</noexport>", "" )
             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
         end
        r = plaintext._main(r)
         if r:find( "&", 1, true ) then
         if r:find( "&", 1, true ) then
             r = mw.text.decode( r )
             r = mw.text.decode( r )
            if r:find( "&shy;", 1, true ) then
                r = r:gsub( "&shy;", "" )
            end
         end
         end
     end
     end
Line 865: Line 1,670:
             if scope then
             if scope then
                 s = type( v )
                 s = type( v )
                 if s == "string" then
                 if s == "string"  and  k ~= "format" then
                     v = mw.text.trim( v )
                     v = mw.text.trim( v )
                 end
                 end
Line 871: Line 1,676:
                     if scope:find( "I18N", 1, true ) then
                     if scope:find( "I18N", 1, true ) then
                         if s == "string" then
                         if s == "string" then
                             elem = handleNoexportWhitespace( v )
                             elem = fair( v )
                         else
                         elseif s == "table" then
                             local translated
                             local translated
                             v, translated = faraway( v )
                             v, translated = faraway( v )
Line 878: Line 1,683:
                                 if translated  and
                                 if translated  and
                                   k == "description" then
                                   k == "description" then
                                     elem = { [ 1 ] = handleNoexportWhitespace( v ),
                                     elem = { [ 1 ] = fair( v ),
                                             [ 2 ] = translated }
                                             [ 2 ] = translated }
                                 else
                                 else
                                     elem = handleNoexportWhitespace( v )
                                     elem = fair( v )
                                 end
                                 end
                             else
                             else
Line 887: Line 1,692:
                             end
                             end
                         end
                         end
                         if v then
                         if type( v ) == "string" then
                             if scope:find( "nowiki", 1, true ) 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 = mw.text.nowiki( v )
                                elem = elem:gsub( "&#13;\n", "<br>" )
                                v    = v:gsub( string.char( 13 ),  "" )
                             else
                             else
                                 v = flat( v )
                                 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
                         end
                         end
Line 899: Line 1,721:
                             elem = nil
                             elem = nil
                         elseif k == "format"  and  not access then
                         elseif k == "format"  and  not access then
                             v    = mw.text.decode( v )
                             elem = mw.text.decode( v )
                             elem = v
                             v    = nil
                         elseif k == "inherits" then
                         elseif k == "inherits" then
                             elem = v
                             elem = v
Line 908: Line 1,730:
                             Data.heirs[ slot ] = v
                             Data.heirs[ slot ] = v
                             v                  = nil
                             v                  = nil
                        elseif k == "style" then
                            elem = v
                            v    = nil
                         elseif s == "string" then
                         elseif s == "string" then
                             v    = mw.text.nowiki( v )
                             v    = mw.text.nowiki( v )
Line 917: Line 1,742:
                     if type( elem ) ~= "nil" then
                     if type( elem ) ~= "nil" then
                         if not target then
                         if not target then
                            if access then
                            if access then
                                if not Data.tree.params then
                                if not Data.tree.params then
                                    Data.tree.params = { }
                                    Data.tree.params = { }
                                end
                                end
                                Data.tree.params[ slot ] = { }
                                Data.tree.params[ slot ] = { }
                                target = Data.tree.params[ slot ]
                                target = Data.tree.params[ slot ]
                            else
                            else
                                Data.tree = { }
                                Data.tree = { }
                                target    = Data.tree
                                target    = Data.tree
                            end
                            end
                         end
                         end
                         target[ k ] = elem
                         target[ k ] = elem
Line 934: Line 1,759:
                         if not tag then
                         if not tag then
                             if access then
                             if access then
                                 if not Data.params then
                                 if type( v ) == "string"  and
                                     Data.params = { }
                                  v.sub( 1, 1 ) == "=" then
                                    v = nil
                                else
                                    if not Data.params then
                                        Data.params = { }
                                    end
                                     Data.params[ slot ] = { }
                                    tag = Data.params[ slot ]
                                 end
                                 end
                                Data.params[ slot ] = { }
                                tag = Data.params[ slot ]
                             else
                             else
                                 Data.tag = { }
                                 Data.tag = { }
Line 944: Line 1,774:
                             end
                             end
                         end
                         end
                         tag[ k ] = v
                         if type( v ) ~= "nil"  and
                          k ~= "suggestedvalues" then
                            tag[ k ] = v
                        end
                     end
                     end
                 else
                 else
Line 955: Line 1,788:
             end
             end
         end -- for k, v
         end -- for k, v
        if not access  and Data.got.sets then
            fellows()
        end
     else
     else
         Fault( f() .. " needs to be of <code>object</code> type" )
         Fault( f() .. " needs to be of <code>object</code> type" )
Line 963: Line 1,799:


local function format()
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
     -- Build presented documentation
     -- Returns <div>
     -- Returns <div>
     local r = mw.html.create( "div" )
     local r = mw.html.create( "div" )
     local s = feasible( Data.tree, true )
     local x = fashioned( Data.tree, true, r )
     if s then
    local s
         r:node( s )
     if x then
         r = x
     end
     end
     if Data.leading then
     if Data.leading then
         local toc = mw.html.create( "div" )
         local toc = mw.html.create( "div" )
        local shift
         if Config.suppressTOCnum then
         if Config.suppressTOCnum then
             toc:addClass( Config.suppressTOCnum )
             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
         end
         toc:css( "margin-top", "0.5em" )
         toc:addClass( "navigation-not-searchable" )
          :css( "margin-top", "0.5em" )
           :wikitext( "__TOC__" )
           :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()
         r:newline()
         :node( toc )
         :node( toc )
         :newline()
         :newline()
        if shift then
            r:node( mw.html.create( "div" )
                          :css( "clear", shift ) )
            :newline()
        end
     end
     end
     s = features()
     s = features()
     if s then
     if s then
         if Data.leading then
         if Data.leading then
             r:node( mw.html.create( "h2" )
             r:node( mw.html.create( "h" .. Config.nested )
                           :wikitext( getLocalizedText( "doc-params" ) ) )
                           :wikitext( factory( "doc-params" ) ) )
             :newline()
             :newline()
         end
         end
         r:node( s )
         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
     end
     if Data.tree and Data.tree.format then
     if Data.tree and Data.tree.format then
         local e, style
         local e = format()
        s = Data.tree.format:lower( Data.tree.format )
         if e then
         if s == "inline" or  s == "block" then
            local show = "Format"
             style = "i"
            if Config.supportFormat then
        else
                show = string.format( "[[%s|%s]]",
            style = "code"
                                      Config.supportFormat, show )
            end
             r:node( mw.html.create( "p" )
                          :addClass( "navigation-not-searchable" )
                          :wikitext( show .. ": " )
                          :node( e ) )
         end
         end
        r:node( mw.html.create( "p" )
                      :wikitext( "Format: " )
                      :node( mw.html.create( style )
                                    :wikitext( s ) ) )
     end
     end
     return r
     return r
end -- format()
end -- formatter()




Line 1,010: Line 2,068:
local function free()
local function free()
     -- Remove JSON comment lines
     -- Remove JSON comment lines
     Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([},\"'])",
     if Data.source:find( "//", 1, true ) then
                      "%1%3" )
        Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([{},\"'])",
                          "%1%3" )
    end
end -- free()
end -- free()


Line 1,017: Line 2,077:


local function full()
local function full()
     -- Build HTML table for display from JSON data, and append an invisible
     -- Build survey table from JSON data, append invisible <templatedata>
    -- <templatedata> block.
     Data.div = mw.html.create( "div" )
     Data.div = mw.html.create( "div" )
                       :addClass( "mw-templatedata-doc-wrap" )
                       :addClass( "mw-templatedata-doc-wrap" )
    if Permit.css.bg then
        Data.div:css( Permit.css.bg )
    end
    if Permit.css.fg then
        Data.div:css( Permit.css.fg )
    end
     focus()
     focus()
     if Data.tag then
     if Data.tag then
Line 1,032: Line 2,097:
         end
         end
     end
     end
     Data.div:node( format() )
     Data.div:node( formatter() )
     if not Data.lazy then
     if not Data.lazy then
         Data.slim = flush()
         Data.slim = flush()
Line 1,043: Line 2,108:
             div:wikitext( Data.strip )
             div:wikitext( Data.strip )
             if Config.loudly then
             if Config.loudly then
            -- Display raw templatedata table all the time.
                 Data.div:node( mw.html.create( "hr" )
                 Data.div:node( mw.html.create( "hr" ) )
                                      :css( { height = "7ex" } ) )
                Data.div:node( div )
             else
             else
            -- Creates an expand link to check raw templatedata table.
                div:css( "display", "none" )
            local wrapper = mw.html.create( "div" )
                wrapper:addClass( "mw-collapsible" )
                wrapper:addClass( "mw-collapsed" )
                wrapper:css( "font-size", "85%" )
                div:addClass( "mw-collapsible-content" )
                wrapper:wikitext( "'''Test of raw TemplateData output''': " )
                wrapper:node( div )
                Data.div:node( wrapper )
             end
             end
            Data.div:node( div )
         end
         end
    end
    if Data.lasting then
        Fault( "deprecated type syntax" )
    end
    if Data.less then
        Fault( Config.solo )
     end
     end
end -- full()
end -- full()
Line 1,064: Line 2,127:


local function furnish( adapt, arglist )
local function furnish( adapt, arglist )
     -- Called by f, this function is the first to do any real work when the
     -- Analyze transclusion
    -- module is invoked.
     -- Parameter:
     -- Parameter:
     --    adapt    -- table, #invoke parameters
     --    adapt    -- table, #invoke parameters
     --    arglist  -- table, template parameters
     --    arglist  -- table, template parameters
     -- Returns string
     -- Returns string
--local spy=""
     local source
     local source
     for k, v in pairs( Config ) do
    favorize()
    -- deprecated:
     for k, v in pairs( Config.basicCnf ) do
         if adapt[ k ]  and  adapt[ k ] ~= "" then
         if adapt[ k ]  and  adapt[ k ] ~= "" then
             Config[ v ] = adapt[ k ]
             Config[ v ] = adapt[ k ]
         end
         end
     end -- for k, v
     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 )
     Config.loudly = faculty( arglist.debug or adapt.debug )
--if mw.site.server:find( "//de.wikipedia.beta.wmflabs.org", 1, true ) then
--    Config.loudly  = true
--end
     Data.lazy    = faculty( arglist.lazy )  and  not Config.loudly
     Data.lazy    = faculty( arglist.lazy )  and  not Config.loudly
     Data.leading  = faculty( arglist.TOC )
     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
     if arglist.JSON then
         source = arglist.JSON
         source = arglist.JSON
elseif _ne(arglist.TNT) then
    elseif arglist.Global then
local tnt = require("Module:TNT")
        source = TemplateData.getGlobalJSON( arglist.Global,
source = tnt.getTemplateData("Templatedata/"
                                            arglist.Local )
.. mw.text.trim(arglist.TNT))
     elseif arglist[ 1 ] then
     elseif arglist[ 1 ] then
         local s    = mw.text.trim( arglist[ 1 ] )
         local s    = mw.text.trim( arglist[ 1 ] )
Line 1,097: Line 2,169:
             source = s
             source = s
         elseif mw.ustring.sub( s, 1, 8 ) ==
         elseif mw.ustring.sub( s, 1, 8 ) ==
               mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then -- <DEL> ' " ` U N I Q
               mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then
             Data.strip = s
             Data.strip = s
         end
         end
    end
    if type( arglist.vertical ) == "string"  and
      arglist.vertical:match( "^%d*%.?%d+[emprx]+$" ) then
        Data.scroll = arglist.vertical
     end
     end
     if not source then
     if not source then
Line 1,105: Line 2,181:
         source = find()
         source = find()
         if not source  and
         if not source  and
          Config.subpage  and  Config.suffix  and
           not Data.title.text:match( Config.subpage ) then
           not Data.title.text:match( Config.subpage ) then
             local s = string.format( Config.suffix,
             local s = string.format( Config.suffix,
Line 1,114: Line 2,189:
             end
             end
         end
         end
--if source  and
--          ( source:find( "|", 1, true ) or
--            source:find( "}}", 1, true ) ) then
--                      --  <ref
--spy=string.format( "[[category:%s]]", Config.strange )
--end
     end
     end
     if not Data.lazy and  Config.subpage then
     if not Data.lazy then
         if not Data.title then
         if not Data.title then
             Data.title = mw.title.getCurrentTitle()
             Data.title = mw.title.getCurrentTitle()
Line 1,127: Line 2,196:
         Data.lazy = Data.title.text:match( Config.subpage )
         Data.lazy = Data.title.text:match( Config.subpage )
     end
     end
     TemplateData.getPlainJSON( source )
     if type( source ) == "string" then
     return finalize()
        TemplateData.getPlainJSON( source )
--return spy .. finalize()
    end
     return finalize( faculty( arglist.source ) )
end -- furnish()
end -- furnish()






TemplateData.failsafe = function ( assert )
Failsafe.failsafe = function ( atleast )
-- Checks the age of this implementation against some minimum ("assert").
    -- 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
     local r
     if not assert or  assert <= TemplateData.serial then
     if last  or  link  or  linked  or  since == "wikidata" then
         r = TemplateData.serial
        local item = Failsafe.item
     else
        since = false
         r = 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
     end
     return r
     return r
end -- TemplateData.failsafe()
end -- TemplateData.getGlobalJSON()




Line 1,153: Line 2,294:
     -- Returns string, or not
     -- Returns string, or not
     if type( adapt ) == "string" then
     if type( adapt ) == "string" then
        local JSONutil = Fetch( "JSONutil", true )
         Data.source = adapt
         Data.source = adapt
         free()
         free()
         Data.got = mw.text.jsonDecode( Data.source )
         if JSONutil then
         if Data.got 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()
             full()
             if Data.lasting then
        elseif not Data.strip then
                 Fault( "deprecated type syntax" )
            local scream = type( Data.got )
             if scream == "string" then
                scream = Data.got
            else
                 scream = "Data.got: " .. scream
             end
             end
            if Data.less then
             Fault( "fatal JSON error: " .. scream )
                Fault( Config.solo )
            end
        elseif not Data.strip then
             Fault( "fatal JSON error" )
         end
         end
     end
     end
Line 1,184: Line 2,336:


p.f = function ( frame )
p.f = function ( frame )
     -- The entry point for templates invoking the module.
     -- Template call
    -- Just wraps furnish in an exception handler.
     local lucky, r
     local lucky, result
     TemplateData.frame = frame
     TemplateData.frame = frame
     lucky, result = pcall( furnish, frame.args, frame:getParent().args )
     lucky, r = pcall( furnish, frame.args, frame:getParent().args )
     if not lucky then
     if not lucky then
         Fault( "INTERNAL: " .. result )
         Fault( "INTERNAL: " .. r )
         result = failures()
         r = failures()
     end
     end
     return result
     return r
end -- p.f()
end -- p.f


p.failsafe = function ( frame )
p.failsafe = function ( frame )
Line 1,211: Line 2,362:
         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 ()
Anonymous user