Module:Format TemplateData: Difference between revisions
Richardpruen (talk | contribs) m 1 revision imported  | 
				Richardpruen (talk | contribs) m 1 revision imported: Templates and CSS files  | 
				||
| (3 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
local TemplateData = { serial = "  | local TemplateData = { suite  = "TemplateData",  | ||
                       serial = "2022-03-10",  | |||
                        item   = 46997995 }  | |||
--[=[  | --[==[  | ||
improve template:TemplateData  | improve template:TemplateData  | ||
]=]  | ]==]  | ||
local Failsafe = TemplateData  | |||
local Config = {  | local Config = {  | ||
     -- multiple   |      -- multiple option names mapped into unique internal fields  | ||
     basicCnf = { catProblem          = "strange",  | |||
                 classMultiColumns   = "selMultClm",  | |||
--   |                  classNoNumTOC       = "suppressTOCnum",  | ||
                 classTable          = "classTable",  | |||
                 cssParWrap          = "cssTabWrap",  | |||
                 cssParams           = "cssTable",  | |||
                 docpageCreate       = "suffix",  | |||
                 docpageDetect       = "subpage",  | |||
                 helpBoolean         = "support4boolean",  | |||
                 helpContent         = "support4content",  | |||
                 helpDate            = "support4date",  | |||
                 helpFile            = "support4wiki-file-name",  | |||
                 helpFormat          = "supportFormat",  | |||
                 helpLine            = "support4line",  | |||
                 helpNumber          = "support4number",  | |||
                 helpPage            = "support4wiki-page-name",  | |||
                 helpString          = "support4string",  | |||
                 helpTemplate        = "support4wiki-template-name",  | |||
                 helpURL             = "support4url",  | |||
                 helpUser            = "support4wiki-user-name",  | |||
                 msgDescMiss         = "solo",  | |||
                 tStylesTOCnum       = "stylesTOCnum",  | |||
                 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  | ||
     cssTable       = false,    -- styles for params table  | |||
     cssTabWrap     = false,    -- styles for params table wrapper  | |||
    debug          = false,  | |||
     subpage        = false,    -- pattern to identify subpage  |      subpage        = false,    -- pattern to identify subpage  | ||
     suffix         = false,   |      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   =   |     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 = {  | ||
     builder = { after           = "block",  | |||
                 suggested   = "  |                 align           = "block",  | ||
                 optional    = "  |                 block           = "block",  | ||
                 deprecated  = "  |                 compressed      = "block",  | ||
                dense           = "block",  | |||
     params  = { aliases   |                 grouped         = "inline",  | ||
                 autovalue   |                 half            = "inline",  | ||
                 default   |                 indent          = "block",  | ||
                 deprecated   |                 inline          = "inline",  | ||
                 description = "string table I18N",  |                 last            = "block",  | ||
                 example   |                 lead            = "block",  | ||
                 label   |                 newlines        = "*",  | ||
                 inherits   |                 spaced          = "inline" },  | ||
                 required   |     colors  = { bg          = "FFFFFF",  | ||
                 suggested   |                 fg          = "000000",  | ||
                 suggestedvalues = "table",  |                 tableheadbg = "B3B7FF",  | ||
                 type   |                 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: | ||
}  | }  | ||
local function Fault( alert )  | local function Fault( alert )  | ||
| Line 113: | Line 140: | ||
end -- Fault()  | end -- Fault()  | ||
----------------------------------  | |||
local function Fetch( ask, allow )  | |||
    -- Fetch module  | |||
    -- Parameter:  | |||
    --     ask    -- string, with name  | |||
    --                       "/global"  | |||
    --                       "JSONutil"  | |||
    --                       "Multilingual"  | |||
    --                       "Text"  | |||
    --                       "WLink"  | |||
    --     allow  -- true: no error if unavailable  | |||
    -- Returns table of module  | |||
    -- error: Module not available  | |||
    local sign = ask  | |||
    local r, stem  | |||
    if sign:sub( 1, 1 ) == "/" then  | |||
        sign = TemplateData.frame:getTitle() .. sign  | |||
    else  | |||
        stem = sign  | |||
        sign = "Module:" .. stem  | |||
    end  | |||
    if TemplateData.extern then  | |||
        r = TemplateData.extern[ sign ]  | |||
    else  | |||
        TemplateData.extern = { }  | |||
    end  | |||
    if not r then  | |||
        local lucky, g = pcall( require, sign )  | |||
        if type( g ) == "table" then  | |||
            if stem  and  type( g[ stem ] ) == "function" then  | |||
                r = g[ stem ]()  | |||
            else  | |||
                r = g  | |||
            end  | |||
            TemplateData.extern[ sign ] = r  | |||
        elseif not allow then  | |||
            error( string.format( "Fetch(%s) %s", sign, g ), 0 )  | |||
        end  | |||
    end  | |||
    return r  | |||
end -- Fetch()  | |||
local function Foreign()  | |||
    -- Guess human language  | |||
    -- Returns slang, or not  | |||
    if type( Data.slang ) == "nil" then  | |||
        local Multilingual = Fetch( "Multilingual", true )  | |||
        if Multilingual  and  | |||
           type( Multilingual.userLangCode ) == "function" then  | |||
            Data.slang = Multilingual.userLangCode()  | |||
        else  | |||
            Data.slang = mw.language.getContentLanguage():getCode()  | |||
                                                         :lower()  | |||
        end  | |||
    end  | |||
    if Data.slang  and  | |||
       mw.ustring.codepoint( Data.slang, 1, 1 ) > 122 then  | |||
        Data.slang = false  | |||
    end  | |||
    return Data.slang  | |||
end -- Foreign()  | |||
local function facet( ask, at )  | local function facet( ask, at )  | ||
| Line 132: | Line 217: | ||
                                    :gsub( "([%-.()+*?^$%[%]])",  |                                     :gsub( "([%-.()+*?^$%[%]])",  | ||
                                           "%%%1" ) )  |                                            "%%%1" ) )  | ||
     local i, k = Data.source:find(   |      local i, k, r, slice, source  | ||
    if not Data.jump then  | |||
        Data.jump = Data.source:find( "params", 2 )  | |||
        if Data.jump then  | |||
            Data.jump = Data.jump + 7  | |||
        else  | |||
            Data.jump = 1  | |||
        end  | |||
    end  | |||
     i, k = Data.source:find( seek,  at + Data.jump )  | |||
     while i  and  not r do  |      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   | 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  | ||
     local o = mw.message.new( "templatedata-" .. adapt )  | |||
end --   |     if Foreign() then  | ||
        o:inLanguage( Data.slang )  | |||
    end  | |||
    return o:plain()  | |||
end -- factory()  | |||
| Line 204: | Line 363: | ||
local function   | local function fair( adjust )  | ||
     --   |      -- Reduce text to one line of plain text, or noexport wikitext blocks  | ||
     --     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   |      if Data.source then  | ||
         local   |          local support = Config.jsonDebug  | ||
         local   |          local css  | ||
        if advance then  | |||
            css = { height = "6em",  | |||
             if   |                     resize = "vertical" }  | ||
            r   = { [ 1 ] = "syntaxhighlight",  | |||
                    [ 2 ] = Data.source,  | |||
                    lang  = "json",  | |||
                    style = table.concat( css, ";" ) }  | |||
             if alert then  | |||
                 r.class( support )  | |||
             end  |              end  | ||
             r = TemplateData.frame:callParserFunction( "#tag", r )  | |||
        else  | |||
             css = { [ "font-size" ]   = "77%",  | |||
                    [ "line-height" ] = "1.35" }  | |||
            if alert then  | |||
                 css.resize = "vertical"  | |||
             else  |              else  | ||
                 css.display = "none"  | |||
             end  |              end  | ||
         end   |             r = mw.html.create( "pre" )  | ||
         r =   |                        :addClass( support )  | ||
                       :css( css )  | |||
                       :wikitext( mw.text.encode( Data.source ) )  | |||
            r = tostring( r )  | |||
         end  | |||
         r = "\n".. r  | |||
     else  |      else  | ||
         r =   |          r = ""  | ||
     end  |      end  | ||
     return r  |      return r  | ||
end --   | end -- fancy()  | ||
local function faraway( alternatives )  | local function faraway( alternatives )  | ||
     -- Retrieve   |      -- 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  | ||
     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  | ||
         local Multilingual = Fetch( "Multilingual", true )  | |||
             if   |         if Multilingual  and  | ||
           type( Multilingual.i18n ) == "function" then  | |||
            local show, slang = Multilingual.i18n( variants )  | |||
             if show then  | |||
                 r1 = show  | |||
                 variants[ slang ] = nil  | |||
                r2 = variants  | |||
             end  |              end  | ||
         end   |          end  | ||
         if not r1 then  |          if not r1 then  | ||
             Foreign()  | |||
             for k, v in pairs( variants ) do  |              for k, v in pairs( variants ) do  | ||
                 if v   |                  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  | ||
                 end  |                  end  | ||
             end -- for k, v  |              end -- for k, v  | ||
         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   |                  if v  and  not Multilingual.isLang( k, true ) then  | ||
                    Fault( string.format( "%s <code>lang=%s</code>",  | |||
                                          "Invalid",  | |||
                                          k ) )  | |||
                 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  | ||
            for k, v in pairs( Data.heirs ) do  | |||
                if v  and  not Data.heirs[ v ] then  | |||
                    n               = n - 1  | |||
                    t[ k ].inherits = nil  | |||
                    Data.heirs[ k ] = nil  | |||
                    p2              = { }  | |||
                    t2              = { }  | |||
                     if p[ v ] then  | |||
                        for k2, v2 in pairs( p[ v ] ) do  | |||
                             p2[ k2 ] = v2  |                              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  | ||
                        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  | ||
            end -- for k, v  | |||
         end  | |||
         end   | |||
     end -- i = 1, n  |      end -- i = 1, n  | ||
     if n > 0 then  |      if n > 0 then  | ||
| Line 380: | Line 669: | ||
local function   | local function favorize()  | ||
     --   |      -- Local customization issues  | ||
     -  |      local boole  = { ["font-size"] = "125%" }  | ||
    local l, cx = pcall( mw.loadData,  | |||
     -  |                          TemplateData.frame:getTitle() .. "/config" )  | ||
    local scripting, style  | |||
    TemplateData.ltr = not mw.language.getContentLanguage():isRTL()  | |||
     if TemplateData.ltr then  | |||
        scripting = "left"  | |||
         if   |     else  | ||
        scripting = "right"  | |||
     end  | |||
     boole[ "margin-" .. scripting ] = "3em"  | |||
     Permit.boole = { [false] = { css  = boole,  | |||
                                 lead = true,  | |||
                                 show = "☐" },  | |||
                     [true]  = { css  = boole,  | |||
                                 lead = true,  | |||
                                 show = "☑" } }  | |||
    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  | ||
             style = "background-color"  | |||
        end  | |||
             if   |         Permit.css[ k ] = { }  | ||
        Permit.css[ k ][ style ] = "#" .. v  | |||
    end -- for k, v  | |||
    if type( cx ) == "table" then  | |||
        local c, s  | |||
        if type( cx.permit ) == "table" then  | |||
             if type( cx.permit.boole ) == "table" then  | |||
                 if type( cx.permit.boole[ true ] ) == "table" then  | |||
                    Permit.boole[ false ]  = cx.permit.boole[ false ]  | |||
                end  | |||
                if type( cx.permit.boole[ true ] ) == "table" then  | |||
                     Permit.boole[ true ]  = cx.permit.boole[ true ]  | |||
                end  | |||
            end  | |||
            if type( cx.permit.css ) == "table" then  | |||
                for k, v in pairs( cx.permit.css ) do  | |||
                    if type( v ) == "table" then  | |||
                        Permit.css[ k ] = v  | |||
                    end  | |||
                end -- for k, v  | |||
             end  |              end  | ||
         end  |          end  | ||
        for k, v in pairs( Config.basicCnf ) do  | |||
            s = type( cx[ k ] )  | |||
            if s == "string"  or  s == "table" then  | |||
                Config[ v ] = cx[ k ]  | |||
            end  | |||
         end -- for k, v  | |||
     end  |      end  | ||
     if   |      if type( Config.subpage ) ~= "string"  or  | ||
       type( Config.suffix ) ~= "string" then  | |||
         local got = mw.message.new( "templatedata-doc-subpage" )  | |||
        local suffix  | |||
        if got:isDisabled() then  | |||
            suffix = "doc"  | |||
         else  |          else  | ||
             suffix = got:plain()  | |||
        end  | |||
        if type( Config.subpage ) ~= "string" then  | |||
            Config.subpage = string.format( "/%s$", suffix )  | |||
        end  | |||
        if type( Config.suffix ) ~= "string" then  | |||
            Config.suffix = string.format( "%%s/%s", suffix )  | |||
         end  |          end  | ||
     end  |      end  | ||
     return   | 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  | ||
             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(    |              Fault(  factory( "invalid-value" ):gsub( "$1", s )  )  | ||
             legal = false  |              legal = false  | ||
         end  |          end  | ||
| Line 553: | Line 1,157: | ||
     -- description etc.  |      -- description etc.  | ||
     s =   |      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  |          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  | ||
                 dd      = mw.html.create( "dd" )  |                  dd      = mw.html.create( "dd" )  | ||
                 section = factory( "doc-param-" .. s )  | |||
                 if param.type == "boolean"   and  | |||
                   ( show == "0" or show == "1" ) then  | |||
                 if param.type == "boolean"   |                     local boole = Permit.boole[ ( show == "1" ) ]  | ||
                    if boole.lead == true then  | |||
                        dd:node( mw.html.create( "code" )  | |||
                                        :wikitext( show ) )  | |||
                          :wikitext( " " )  | |||
                    end  | |||
                    if type( boole.show ) == "string" then  | |||
                        local v = mw.html.create( "span" )  | |||
                                         :attr( "aria-hidden", "true" )  | |||
                                         :wikitext( boole.show )  | |||
                        if boole.css then  | |||
                             v:css( boole.css )  | |||
                         end  |                          end  | ||
                        dd:node( v )  | |||
                         dd:wikitext(  |                      end  | ||
                    if type( boole.suffix ) == "string" then  | |||
                         dd:wikitext("  |                          dd:wikitext( boole.suffix )  | ||
                     end  | |||
                    if boole.lead == false then  | |||
                         dd:wikitext( " " )  | |||
                          :node( mw.html.create( "code" )  | |||
                                        :wikitext( show ) )  | |||
                     end  |                      end  | ||
                 elseif   |                  elseif s == "suggestedvalues" then  | ||
                     local v, css, class, ts = facilities( param )  | |||
                     if v then  | |||
                        local ul  | |||
                         if   |                          ul, v = feasible( param, access, v )  | ||
                             dd:  |                          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  | ||
                     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   |              if s == "string" then  | ||
                 Data.params[ access ].type = s  |                  Data.params[ access ].type = s  | ||
                 typed:wikitext(   |                  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 =   |                 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(    |              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(   |                     :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 =   |      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 ) )  | ||
      :  |       :css( Permit.css[ status ] )  | ||
     :addClass( styles .. status )  | |||
      :node( begin )  |       :node( begin )  | ||
      :node( code )  |       :node( code )  | ||
      :node( desc )  |       :node( desc )  | ||
      :node( typed )  |       :node( typed )  | ||
      :node(   |       :node( eager )  | ||
      :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 tbl = mw.html.create( "table" )  | |||
         local tbl   |          local tr  = mw.html.create( "tr" )  | ||
         local 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 type( Config.classTable ) == "table" then  | |||
            for k, v in pairs( Config.classTable ) do  | |||
--   |                 tbl:addClass( v )  | ||
         if Config.  |             end -- for k, v  | ||
             tbl:  |         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" )  | ||
                         :  |                          :css( Permit.css.tablehead )  | ||
                         :wikitext(   |                          :wikitext( factory( "doc-param-name" ) ) )  | ||
           :node( mw.html.create( "th" )  |            :node( mw.html.create( "th" )  | ||
                         :  |                          :css( Permit.css.tablehead )  | ||
                         :wikitext(   |                          :wikitext( factory( "doc-param-desc" ) ) )  | ||
           :node( mw.html.create( "th" )  |            :node( mw.html.create( "th" )  | ||
                         :  |                          :css( Permit.css.tablehead )  | ||
                         :wikitext(   |                          :wikitext( factory( "doc-param-type" ) ) )  | ||
           :node( mw.html.create( "th" )  |            :node( mw.html.create( "th" )  | ||
                         :  |                          :css( Permit.css.tablehead )  | ||
                         :wikitext(   |                          :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(   |                  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.  |          if Config.cssTabWrap or Data.scroll then  | ||
             r = mw.html.create( "div" )  |              r = mw.html.create( "div" )  | ||
            if type( Config.cssTabWrap ) == "table" then  | |||
                r:css( Config.cssTabWrap )  | |||
            elseif type( Config.cssTabWrap ) == "string" then  | |||
                -- deprecated  | |||
                r:cssText( Config.cssTabWrap )  | |||
            end  | |||
            if Data.scroll then  | |||
                r:css( "height",   Data.scroll )  | |||
                 :css( "overflow", "auto" )  | |||
            end  | |||
            r:node( tbl )  | |||
         else  |          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   |      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>  |              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  | ||
         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( "­", 1, true ) then  | |||
                r = r:gsub( "­", "" )  | |||
            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 =   |                              elem = fair( v )  | ||
                         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 ] =   |                                      elem = { [ 1 ] = fair( v ),  | ||
                                              [ 2 ] = translated }  |                                               [ 2 ] = translated }  | ||
                                 else  |                                  else  | ||
                                     elem =   |                                      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( "
\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  | ||
                             elem = mw.text.decode( 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 not Data.tree.params then  | |||
                                    Data.tree.params = { }  | |||
                                end  | |||
                                Data.tree.params[ slot ] = { }  | |||
                                target = Data.tree.params[ slot ]  | |||
                            else  | |||
                                Data.tree = { }  | |||
                                target    = Data.tree  | |||
                            end  | |||
                         end  |                          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  | ||
                             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", "\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", "\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   |      local x = fashioned( Data.tree, true, r )  | ||
     if   |     local s  | ||
         r  |      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( "  |              r:node( mw.html.create( "h" .. Config.nested )  | ||
                            :wikitext(   |                             :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  |          local e = format()  | ||
         if e then  | |||
         if   |             local show = "Format"  | ||
            if Config.supportFormat then  | |||
                show = string.format( "[[%s|%s]]",  | |||
                                      Config.supportFormat, show )  | |||
            end  | |||
             r:node( mw.html.create( "p" )  | |||
                           :addClass( "navigation-not-searchable" )  | |||
                           :wikitext( show .. ": " )  | |||
                           :node( e ) )  | |||
         end  |          end  | ||
     end  |      end  | ||
     return r  |      return r  | ||
end --   | 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  | ||
        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   |      -- Build survey table from JSON data, append invisible <templatedata>  | ||
     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(   |      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  | ||
                 Data.div:node( mw.html.create( "hr" )  | |||
                 Data.div:node( mw.html.create( "hr"   |                                       :css( { height = "7ex" } ) )  | ||
             else  |              else  | ||
                div:css( "display", "none" )  | |||
             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 )  | ||
     --   |      -- Analyze transclusion  | ||
     -- Parameter:  |      -- Parameter:  | ||
     --     adapt    -- table, #invoke parameters  |      --     adapt    -- table, #invoke parameters  | ||
     --     arglist  -- table, template parameters  |      --     arglist  -- table, template parameters  | ||
     -- Returns string  |      -- Returns string  | ||
     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 )  | ||
     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 arglist.Global then  | |||
        source = TemplateData.getGlobalJSON( arglist.Global,  | |||
                                             arglist.Local )  | |||
     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   |                 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  | ||
            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  | ||
     end  |      end  | ||
     if not Data.lazy   |      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 )  | ||
    end  | |||
     return finalize( faculty( arglist.source ) )  | |||
end -- furnish()  | end -- furnish()  | ||
Failsafe.failsafe = function ( atleast )  | |||
    -- Retrieve versioning and check for compliance  | |||
    -- Precondition:  | |||
    --     atleast  -- string, with required version  | |||
    --                         or wikidata|item|~|@ or false  | |||
    -- Postcondition:  | |||
    --     Returns  string  -- with queried version/item, also if problem  | |||
    --              false   -- if appropriate  | |||
    -- 2020-08-17  | |||
    local since  = atleast  | |||
    local last   = ( since == "~" )  | |||
    local linked = ( since == "@" )  | |||
    local link   = ( since == "item" )  | |||
     local r  |      local r  | ||
     if not   |      if last  or  link  or  linked  or  since == "wikidata" then  | ||
         r = TemplateData.  |         local item = Failsafe.item  | ||
        since = false  | |||
         r =   |         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.  | 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  |          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.  |         elseif not Data.strip then  | ||
            local scream = type( Data.got )  | |||
             if scream == "string" then  | |||
                scream = Data.got  | |||
            else  | |||
                 scream = "Data.got: " .. scream  | |||
             end  |              end  | ||
             Fault( "fatal JSON error: " .. scream )  | |||
             Fault( "fatal JSON error" )  | |||
         end  |          end  | ||
     end  |      end  | ||
| Line 1,184: | Line 2,336: | ||
p.f = function ( frame )  | p.f = function ( frame )  | ||
     --   |      -- Template call  | ||
     local lucky, r  | |||
     local lucky,   | |||
     TemplateData.frame = frame  |      TemplateData.frame = frame  | ||
     lucky,   |      lucky, r = pcall( furnish, frame.args, frame:getParent().args )  | ||
     if not lucky then  |      if not lucky then  | ||
         Fault( "INTERNAL: " ..   |          Fault( "INTERNAL: " .. r )  | ||
         r = failures()  | |||
     end  |      end  | ||
     return   |      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   |      return Failsafe.failsafe( since )  or  ""  | ||
end -- p.failsafe  | end -- p.failsafe  | ||
p.TemplateData = function ()  | p.TemplateData = function ()  | ||