Modul:WikidataScheme

Vorlagenprogrammierung Diskussionen Lua Unterseiten
Modul Deutsch English

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus

Dies ist die (produktive) Mutterversion eines global benutzten Lua-Moduls.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData: 2019-12-16

local WikidataScheme = { suite   = "WikidataScheme",
                         serial  = "2019-12-16",
                         item    = 74420405,
                         globals = { Multilingual = 47541920 } }
--[=[
Pattern for new entities on Wikidata as internationalized guide
]=]
local Failsafe   = WikidataScheme
local GlobalMod  = WikidataScheme
local Config     = { }
local JSONexport = { }
local Resolver   = { }
local Table      = { }
local Text       = { }



Config.coded    = { dir  = "ltr",
                    lang = "en" }
Config.colors   = { tableheadbg = "B3B7FF",
                    required    = "EAF3FF",
                    suggested   = "FFFFFF",
                    optional    = "EAECF0",
                    deprecated  = "FFCBCB" }
Config.spaces   = { L = "Lexeme:",
                    P = "Property:",
                    Q = false }
Config.types    = { "P", "Q", "L" }
Config.errors   = { bag = false }
Config.i18n     = { onLabel = "Q461984",    -- naming convention
                    onDesc  = "Q1200750",   -- description
                            -- wikibase-newentity-description
                    onAlias = "wikibase-entitytermsforlanguagelistview-aliases" }
                            -- Q18616576   Wikidata property
                            -- Q19798642   Wikidata value
Config.defaults = { err_BadExample      = "Invalid property example",
                                           -- ToDo: I18n
                    err_BadQualifier    = "Invalid qualifier definition",
                    err_InvalidClaim    = "Invalid claim",
                    err_InvalidNameType = "Invalid entity name type",
                    err_NameBad         = "is no assigned entity name",
                    err_NameEmpty       = "entity name empty",
                    err_NameMissed      = "unresolved",
                    err_NoEntity        = "is not an entity",
                    err_NoNameResolver  = "needs",
                    err_NoProperty      = "is not a Property",
                    err_q_qlist         = "Both q and qlist specified",
                    err_Unguided        = "Missing guidance"
                  }



local foreignModule = function ( access, advanced, append, alt, alert )
    -- Fetch global module
    -- Precondition:
    --     access    -- string, with name of base module
    --     advanced  -- true, for require(); else mw.loadData()
    --     append    -- string, with subpage part, if any; or false
    --     alt       -- number, of wikidata item of root; or false
    --     alert     -- true, for throwing error on data problem
    -- Postcondition:
    --     Returns  whatever, probably table
    -- 2019-10-29
    local storage = access
    local finer = function ()
                      if append then
                          storage = string.format( "%s/%s",
                                                   storage,
                                                   append )
                      end
                  end
    local fun, lucky, r, suited
    if advanced then
        fun = require
    else
        fun = mw.loadData
    end
    GlobalMod.globalModules = GlobalMod.globalModules or { }
    suited = GlobalMod.globalModules[ access ]
    if not suited then
        finer()
        lucky, r = pcall( fun,  "Module:" .. storage )
    end
    if not lucky then
        if not suited  and
           type( alt ) == "number"  and
           alt > 0 then
            suited = string.format( "Q%d", alt )
            suited = mw.wikibase.getSitelink( suited )
            GlobalMod.globalModules[ access ] = suited or true
        end
        if type( suited ) == "string" then
            storage = suited
            finer()
            lucky, r = pcall( fun, storage )
        end
        if not lucky and alert then
            error( "Missing or invalid page: " .. storage, 0 )
        end
    end
    return r
end -- foreignModule()



local failures = function ()
    -- Summarize all faults
    -- Postcondition:
    --     Returns  string, hopefully empty
    local r
    if Config.errors.bag then
        local max    = 1000000000
        local id     = math.floor( os.clock() * max )
        local show   = string.format( "%s * Errors",
                                      WikidataScheme.suite )
        local sign   = string.format( "error_%d", id )
        local errors = mw.html.create( "ul" )
                         :addClass( "error" )
        local e      = mw.html.create( "h2" )
                         :addClass( "error" )
                         :attr( "id", sign )
                         :wikitext( show )
        for i = 1, #Config.errors.bag do
            errors:newline()
                  :node( mw.html.create( "li" )
                           :addClass( "error" )
                           :wikitext( Config.errors.bag[ i ] ) )
        end -- for i
        r = string.format( "%s\n%s\n",
                           tostring( e ), tostring( errors ) )
        show = string.format( "[[#%s|%s]]", sign, WikidataScheme.suite )
        mw.addWarning( show )
    end
    Config.errors.bag = false
    return r or ""
end -- failures()



local fault = function ( alert )
    -- Format one error message in HTML
    -- Precondition:
    --     alert  -- string, plain error message
    -- Postcondition:
    --     Returns  string, HTML error message
    local e = mw.html.create( "span" )
                     :addClass( "error" )
                     :wikitext( alert )
    Config.errors.bag = Config.errors.bag  or  { }
    table.insert( Config.errors.bag,  alert or "??fault??" )
    return tostring( e )
end -- fault()



local fix = function ( adjust )
    -- Escape and trim string
    -- Precondition:
    --     adjust  -- string, with some text
    -- Postcondition:
    --     Returns  string, with some text
    return mw.text.trim( adjust ):gsub( "\\", "\\\\" )
                                 :gsub( "\"", "\\\"" )
end -- fix()



Config.feature = function ( area, about, allow )
    local r
    if Config.options then
    end
    return r
end -- Config.feature()



Config.field = function ( ask )
    -- Retrieve general I18n text
    -- Precondition:
    --     ask  -- string, with text ID, entity ID or system message
    -- Postcondition:
    --     Returns  string, with message
    local r
    Config.texts = Config.texts  or  { }
    r = Config.texts[ ask ]
    if not r then
        r = Config.i18n[ ask ]
        if type( r ) == "string" then
            if r:match( "^Q%d+$" ) then
                r = Text.wikibase( r )
            else
                r = mw.message.new( r ):plain()
            end
            Config.texts[ ask ] = r
        end
    end
    return r or "??????????"
end -- Config.field()



Config.first = function ()
    -- Initialize or reset Config
    if not Config.statelist then
        Config.css       = { }
        Config.statelist = { }
        for k, v in pairs( Config.colors ) do
            if k == "tableheadbg" then
                k = "tablehead"
            else
                Config.statelist[ k ] = true
            end
            Config.css[ k ] = { ["background-color"]  =  "#" .. v }
        end -- for k, v
    end
    Config.options = false
    --  aus /config neu, mit .Request überschreiben
end -- Config.first()



JSONexport.family = function ( assign, access, align, adjust )
    -- Create list of claims or qualifiers
    -- Precondition:
    --     assign  -- table, with definition of element
    --     access  -- string, with key in assign
    --     align   -- number, level of alignment
    --     adjust  -- true, for resolving symbolic IDs
    -- Postcondition:
    --     Returns  string with extended JSON code, or not
    local collect = assign[ access ]
    local r
    if type( collect ) == "table" then
        local indent = align + 1
        local n      = #collect
        local shift  = string.rep( "  ", align )
        local list
        if align > 1  and  type( assign.list ) == "boolean" then
            list = assign.list
            r = string.format( "%s\"%s\": %s",
                               shift, tostring( list ) )
        else
            r = ""
        end
        if n > 0 then
            local sep = ""
            if list then
                r = string.format( "%s\n%s", r, shift )
            end
            r = string.format( "%s%s\"%s\": [",
                               r, shift, access )
            for i = 1, n do
                r, sep = JSONexport.fiat( collect[ i ],
                                          indent,
                                          r,
                                          sep,
                                          adjust )
            end -- for i
            r = string.format( "%s\n%s%s    ]",
                               r,  shift,  string.rep( " ", #access ) )
        end
    end
    return r
end -- JSONexport.family()



JSONexport.favour = function ( all )
    -- Create resolve component
    -- Precondition:
    --     all     -- table, with entire request
    local r
    if type( all.resolve ) == "table" then
        local n     = 0
        local order = { }
        local j, coll, s
        for k, v in pairs( all.resolve ) do
            if type( k ) == "string"  and
               type( v ) == "table" then
                k = mw.text.trim( k )
                if k ~= "" then
                    for i = 1, #Config.types do
                        s = Config.types[ i ]
                        j = v[ s ]
                        if type( j ) == "number"  and
                           j >= 0  and
                           j == math.floor( j ) then
                            coll      = coll  or  { }
                            k         = fix( k )
                            coll[ k ] = string.format( "{ \"%s\": %d }",
                                                          s, j )
                            table.insert( order, k )
                            j = mw.ustring.len( k )
                            if j > n then
                                n = j
                            end
                            break -- for i
                        end
                    end -- for i
                end
            end
        end -- for k, v
        if #order > 0 then
            local sep = ""
            local k, v
            table.sort( order )
            r = "  \"resolve\": { "
            n = n + 1
            for i = 1, #order do
                k = order[ i ]
                v = coll[ k ]
                j = mw.ustring.len( k )
                s = string.rep( " ",   n - mw.ustring.len( k ) )
                r = string.format( "%s%s\"%s\": %s%s",
                                     r, sep, k, s, v )
                sep = ",\n               "
            end -- for k, v
            r = r ..  "\n           }"
        end
    end
    return r
end -- JSONexport.favour()



JSONexport.features = function ( assign, align, adjust )
    -- Create value element
    -- Precondition:
    --     assign  -- table, with definition of value
    --     align   -- number, level of alignment
    --     adjust  -- true, for resolving symbolic IDs
    -- Postcondition:
    --     Returns  string, if not empty
    local r
    if type( assign ) == "table" then
        local indent = align + 1
        local shift  = string.rep( "  ", align )
        local f = function ( a )
                      if a  and a ~= "" then
                          if r then
                              r = string.format( "%s,\n%s", r, a )
                          else
                              r = "\n" .. a
                          end
                      end
                  end -- f()
        f( JSONexport.flat( assign, "intro", indent ) )
        f( JSONexport.filled( assign, indent, adjust ) )
        f( JSONexport.fixed( assign, "q", indent, adjust ) )
        f( JSONexport.fixed( assign, "qdefault", indent, adjust ) )
        f( JSONexport.fixed( assign, "qqexample", indent, adjust ) )
        f( JSONexport.flat( assign, "example", indent ) )
        f( JSONexport.family( assign, "qualifiers", indent, adjust ) )
        f( JSONexport.flat( assign, "terminate", indent ) )
        if r then
            r = string.format( "%s   {%s\n%s             }",
                               shift, r, shift )
        end
    end
    return r
end -- JSONexport.features()



JSONexport.fetch = function ( access, adjust, alike )
    -- Resolve symbolic entity name, if necessary
    -- Precondition:
    --     access  -- string, with entity name or ID
    --     adjust  -- true, for resolving symbolic IDs
    --     alike   -- string, if Property or Item required
    -- Postcondition:
    --     Returns: string, if valid, with quoted thing
    local r
    if type( access ) == "string" then
        local s = mw.text.trim( access )
        if s ~= "" then
            if adjust then
                r = Resolver.fetch( s, alike )
            else
                r = s
            end
            if r then
                r = string.format( "\"%s\"",
                                   r:gsub( "\"", "" )
                                    :gsub( "\\", "" ) )
            end
        end
    end
    return r
end -- JSONexport.fetch()



JSONexport.fiat = function ( assign, align, apply, after, adjust )
    -- Create independent entry (in claims or qualifiers) as JSON
    -- Precondition:
    --     assign  -- table, with definition of element
    --     align   -- number, level of alignment
    --     apply   -- string, with JSON code to be extended
    --     after   -- string, with preceding separator
    --     adjust  -- true, for resolving symbolic IDs
    -- Postcondition:
    --     Extend apply if element present
    --     Returns:
    --         1. string with extended JSON code
    --         2. separator, for next element
    local r  = apply
    local r2 = after
    if type( assign ) == "table" then
        local s = JSONexport.fetch( assign.subject, adjust, true )
        if s then
            local shift  = string.rep( "  ", align )
            local indent = align + 1
            local f = function ( a )
                          if a  and a ~= "" then
                              if r then
                                  r = string.format( "%s,\n%s", r, a )
                              else
                                  r = "\n" .. a
                              end
                          end
                      end -- f()
            local state
            r = string.format( "%s%s\n%s{ \"subject\": %s",
                               r, r2, shift, s )
            f( JSONexport.flat( assign, "intro", indent ) )
            if Config.statelist[ assign.state ] then
                state = assign.state
            else
                state = "optional"
            end
            r = string.format( "%s,\n%s  \"state\":   \"%s\"",
                               r, shift, state )
            f( JSONexport.filled( assign, indent, adjust ) )
            f( JSONexport.fixed( assign, "q", indent, adjust ) )
            f( JSONexport.fixed( assign, "qdefault", indent, adjust ) )
            f( JSONexport.fixed( assign, "qqexample", indent, adjust ) )
            if type( assign.values ) == "table" then
                local vals = { }
                local v
                for i = 1, #assign.values do
                    v = JSONexport.features( assign.values[ i ],
                                             indent,
                                             adjust )
                    if v then
                        table.insert( vals, v )
                    end
                end -- for i
                if #vals then
                    local sep = ""
                    r = string.format( "%s,\n%s  \"values\":  [",
                                       r, shift )
                    for i = 1, #vals do
                        r   = string.format( "%s%s\n%s      %s",
                                             r, sep, shift, vals[ i ] )
                        sep = ","
                    end -- for i
                    r = string.format( "%s\n%s             ]", r, shift )
                end
            end
            f( JSONexport.flat( assign, "example", indent ) )
            f( JSONexport.flat( assign, "terminate", indent ) )
            f( JSONexport.further( assign, "class", indent ) )
            f( JSONexport.further( assign, "style", indent ) )
            r = string.format( "%s\n%s}", r, shift )
            r2 = ","
        end
    end
    return r, r2
end -- JSONexport.fiat()



JSONexport.filled = function ( assign, align, adjust )
    -- Create independent JSON list of suggested items (Q), if present
    -- Precondition:
    --     assign  -- table, with definition of element
    --     align   -- number, level of alignment
    --     adjust  -- true, for resolving symbolic IDs
    -- Postcondition:
    --     Returns  string with JSON code, or not
    local r
    if type( assign ) == "table"  and
       type( assign.qlist ) == "table" then
        local n = #assign.qlist
        if n > 0 then
            local indent = align + 1
            local sep    = ""
            local shift  = string.rep( "  ", align )
            local q, s
            r = shift .. "\"qlist\": [ "
            for i = 1, n do
                q = assign.qlist[ i ]
                if type( q ) == "number" then
                    s = tostring( q )
                    if s:find( "^[1-9]%d*$" ) then
                        q = "Q" .. s
                    end
                end
                if type( q ) == "string" then
                    s = JSONexport.fetch( q, adjust, false )
                elseif i == n  and  q == true then
                    s = "true"
                end
                if s then
                    r   = string.format( "%s%s%s",
                                         r, sep, s )
                    sep = ",\n           " .. shift
                end
            end -- for i
            r = r .. " ]"
        end
    end
    return r
end -- JSONexport.filled()



JSONexport.fixed = function ( assign, at, align, adjust )
    -- Create mandatory/default item (Q), if present
    -- Precondition:
    --     assign  -- table, with definition of element
    --     at      -- string, with ID within assign
    --                "q", "qdefault", "qqexample"
    --     align   -- number, level of alignment
    --     adjust  -- true, for resolving symbolic IDs
    -- Postcondition:
    --     Returns  string with JSON code, or not
    local r
    if type( assign ) == "table" then
        local v = assign[ at ]
        local s = type( v )
        if s == "string" then
            s = v
        elseif s == "number" then
            if v > 0  and
               v == math.floor( v ) then
                s = string.format( "Q%d", v )
            else
                s = false
            end
        elseif s == "table" then
            local n = #v
            if n == 1 then
                s = v[ 1 ]
            elseif n > 1 then
                local collect = { }
                local k
                for i = 1, n do
                     s = JSONexport.fetch( v[ i ], adjust, false )
                     if s then
                         table.insert( collect, s )
                         k = i
                     end
                end -- for i
                n = #collect
                if at == "qqexample"  and  n ~= 2 then
                    n = 0
                end
                if n == 1 then
                    s = v[ k ]
                else
                    if n > 1 then
                        local shift = string.rep( "  ", align )
                        local sep   = "\n"
                        r = string.format( "%s\"%s\": [", shift, at )
                        for i = 1, n do
                            r = string.format( "%s%s%s  %s",
                                               r,
                                               sep,
                                               shift,
                                               collect[ i ] )
                            sep = ",\n"
                        end -- for i
                        s = string.rep( " ",  2*align + #at + 4 )
                        r = string.format( "%s\n%s]", r, s )
                    end
                    s = false
                end
            else
                s = false
            end
        else
            s = false
        end
        if s  and  not r  and  at ~= "qqexample" then
            s = JSONexport.fetch( s, adjust, false )
            if s then
                local shift = string.rep( "  ", align )
                r = string.format( "%s\"%s\": %s", shift, at, s )
            end
        end
    end
    return r
end -- JSONexport.fixed()



JSONexport.flat = function ( all, at, align )
    -- Create JSON for textual element
    -- Precondition:
    --     all    -- table, with request branch
    --     at     -- string, with ID within { all }
    --     align  -- number, level of alignment
    -- Postcondition:
    --     Returns  string with extended JSON code
    local e = all[ at ]
    local o = { }
    local s = type( e )
    local r, shift, t
    if s == "table" then
        local parts
        for k, v in pairs( e ) do
            if type( k ) == "string"  and
               k:find( "^%l%l" ) then
                if type( v ) == "string" then
                    if k ~= "class"  and  k ~= "style" then
                        parts = mw.text.split( k, "-" )
                        if parts[ 1 ]:find( "^%l%l%l?$" ) then
                            if #parts >= 2  and
                               not parts[ 2 ]:find( "^%u%l%l%l$" )  and
                               not parts[ 2 ]:find( "^%l%l%l" )  and
                               not parts[ 2 ]:find( "^%u%u$" ) then
                                k = false
                            end
                        else
                            k = false
                        end
                    end
                elseif type( v ) ~= "table"   or
                       ( k ~= "class"  and  k ~= "style" ) then
                    k = false
                end
                if k then
                    t = t  or  { }
                    t[ k ] = v
                    if k ~= "class"  and  k ~= "style" then
                        table.insert( o, k )
                    end
                end
            end
        end -- for k, v
        if t then
            table.sort( o )
            if t.class then
                table.insert( o, "class" )
            end
            if t.style then
                table.insert( o, "style" )
            end
        end
    elseif s == "string" then
        if e:match( "^[PQL]%d+$" ) then
            r = string.format( "\"%s\"", e )
        else
            t = { }
            t.und = e
            table.insert( o, "und" )
        end
    end
    if r or t then
        shift = string.rep( "  ", align )
    end
    if t then
        local sep = ""
        local data, slang
        r = "{\n"
        for i = 1, #o do
            slang = o[ i ]
            data  = t[ slang ]
            if type( data ) == "string" then
                s = fix( data )
            else
                local sept = ""
                s = "{\n"
                for k, v in pairs( data ) do
                    s    = string.format( "%s%s%s    \"%s\": \"%s\"",
                                          s, sept, shift, k, v )
                    sept = ",\n"
                end -- for k, v
                s = string.format( "%s\n%s  %s}",
                                   s,
                                   shift,
                                   string.rep( " ",  #slang + 4 ) )
            end
            r   = string.format( "%s%s%s  \"%s\": \"%s\"",
                                 r, sep, shift, slang, s )
            sep = ",\n"
        end -- for i
        r = string.format( "%s\n%s%s}",
                           r,
                           shift,
                           string.rep( " ",  #at + 4 ) )
    end
    if r then
        r = string.format( "%s\"%s\": %s", shift, at, r )
    end
    return r
end -- JSONexport.flat()



JSONexport.full = function ( all, adjust )
    -- Create independent JSON for registration elements
    -- Precondition:
    --     all     -- table, with entire request
    --     adjust  -- true, for resolving symbolic IDs
    -- Postcondition:
    --     Returns  string with JSON code, or error message
    local r = string.format( "{ \"@generated\": \"%s\"",
                             Config.frame:callParserFunction( "#timel",
                                                              "c" ) )
    local f = function ( a )
                  if a  and a ~= "" then
                      r = string.format( "%s,\n%s", r, a )
                  end
              end -- f()
    r = string.format( "%s,\n  \"@format\": \"%s %s\"",
                       r, WikidataScheme.suite, WikidataScheme.serial )
    f( JSONexport.flat( all, "caption", 1 ) )
    f( JSONexport.flat( all, "onLabel", 1 ) )
    f( JSONexport.flat( all, "onDesc",  1 ) )
    f( JSONexport.flat( all, "onAlias", 1 ) )
    f( JSONexport.family( all, "claims", 1, adjust ) )
    f( JSONexport.flat( all, "footer", 1 ) )
    if not adjust then
        f( JSONexport.favour( all ) )
    end
    f( JSONexport.further( all, "id", 1 ) )
    f( JSONexport.further( all, "class", 1 ) )
    f( JSONexport.further( all, "style", 1 ) )
    f( JSONexport.further( all, "options", 1 ) )
    r = r .. "\n}"
    return r
end -- JSONexport.full()



JSONexport.further = function ( all, at, align )
    -- Create entry for auxilary elements
    -- Precondition:
    --     all    -- table, with entire request
    --     at     -- string, with ID within { all }
    --     align  -- number, level of alignment
    -- Postcondition:
    --     Returns  string with JSON code, or not
    local permit = { id_string     = true,
                     class_string  = true,
                     class_table   = true,
                     style_string  = true,
                     style_table   = true,
                     options_table = true }
    local e = all[ at ]
    local s = type( e )
    local r
    if permit[ string.format( "%s_%s", at, s ) ] then
        if s == "string" then
            e = mw.text.trim( e )
            if e == "" then
                e = false
            elseif at == "class" then
                e = mw.text.split( e, "%s+" )
                s = "table"
            end
        elseif not pairs( e ) then
            e = false
        end
        if e then
            local shift = string.rep( "  ", align )
            if s == "string" then
                r = string.format( "%s%s%s  \"%s\": \"%s\"",
                                   shift,  at,  fix( e ) )
            else
                local sep = ""
                r = "{\n"
                for k, v in pairs( e ) do
                    r   = string.format( "%s%s%s  \"%s\": \"%s\"",
                                         r,  sep,  shift,  k,  fix( e ) )
                    sep = ",\n"
                end -- for k, v
                r = string.format( "%s\n%s%s}",
                                   r,
                                   shift,
                                   string.rep( " ",  #at + 4 ) )
            end
        end
    end
    return r
end -- JSONexport.further()



Resolver.fetch = function ( access, alike )
    -- Resolve entity name
    -- Precondition:
    --     access  -- string, with entity name or ID
    --     alike   -- string, if Property or Item required, "P" or "Q"
    -- Postcondition:
    --     Returns
    --         1. string or not, with entity ID
    --         2. string, with error message
    local r1, r2
    if type( access ) == "string" then
        r2 = mw.text.trim( access )
        if r2 == "" then
            r2 = Text.flip( "err_NameEmpty" )
        else
            if r2:find( "^[PQL]%d+$" ) then
                r1 = r2
            elseif type( WikidataScheme.Request.resolve ) == "table" then
                local entry = WikidataScheme.Request.resolve[ r2 ]
                if type( entry ) == "table" then
                    for k, v in pairs( Config.spaces ) do
                        if type( entry[ k ] ) == "number" then
                            r1 = string.format( "%s%d", k, entry[ k ] )
                            break -- for k, v
                        end
                    end -- for k, v
                    if not r1 then
                        r2 = string.format( "<%s> %s",
                                            r2,
                                            Text.flip( "err_NameBad" )
                                          )
                    end
                else
                    r2 = string.format( "<%s> %s",
                                        r2,
                                        Text.flip( "err_NameMissed" ) )
                end
            else
                r2 = string.format( "<%s> %s .resolve",
                                   r2,
                                   Text.flip( "err_NoNameResolver" ) )
            end
            if r1 then
                if alike  and  r1:sub( 1, 1 ) ~= alike then
                    r2 = string.format( "%s %s",
                                        r1,
                                        Text.flip( "err_InvalidNameType"
                                                 ) )
                elseif not mw.wikibase.isValidEntityId( r1 ) then
                    r2 = string.format( "%s %s",
                                        r1,
                                        Text.flip( "err_NoEntity" ) )
                    r1 = false
                else
                    r2 = false
                end
            end
        end
    else
        r2 = string.format( "%s %s",
                            Text.flip( "err_InvalidNameType" ),
                            type( access ) )
    end
    return r1, r2
end -- Resolver.fetch()



Resolver.fire = function ( all )
    -- Create <table> for resolve assignment
    -- Precondition:
    --     all  -- table, with entire request
    -- Postcondition:
    --     Returns  string with entire HTML table, or not
    local r
    if type( all.resolve ) == "table" then
        local order = { }
        local j, coll, s
        for k, v in pairs( all.resolve ) do
            if type( k ) == "string"  and
               type( v ) == "table" then
                k = mw.text.trim( k )
                if k ~= "" then
                    for i = 1, #Config.types do
                        s = Config.types[ i ]
                        j = v[ s ]
                        if type( j ) == "number"  and
                           j >= 0  and
                           j == math.floor( j ) then
                            coll      = coll  or  { }
                            k         = fix( k )
                            coll[ k ] = string.format( "%s%d", s, j )
                            table.insert( order, k )
                            break -- for i
                        end
                    end -- for i
                end
            end
        end -- for k, v
        if #order > 0 then
            local tbl = mw.html.create( "table" )
                          :addClass( "wikitable sortable" )
                          :addClass( WikidataScheme.suite .. "-resolve" )
            local tr  = mw.html.create( "tr" )
            local e, e2, k, s, td
            tr:newline()
              :node( mw.html.create( "th" )
                            :attr( "title", "symbol" )
                            :css( Config.css.tablehead )
                            :wikitext( "symbol" ) )
              :newline()
              :node( mw.html.create( "th" )
                            :attr( "title", "entity" )
                            :css( Config.css.tablehead )
                            :wikitext( "entity" ) )
              :newline()
              :node( mw.html.create( "th" )
                            :attr( "title", "label" )
                            :css( Config.css.tablehead )
                            :wikitext( "label" ) )
            tbl:newline()
            table.sort( order )
            for i = 1, #order do
                k  = order[ i ]
                tr = mw.html.create( "tr" )
                e  = mw.html.create( "code" )
                            :attr( Config.coded )
                            :wikitext( k )
                td = mw.html.create( "td" )
                            :node( e )
                tr:newline()
                  :node( td )
                e, e2 = Table.fetch( coll[ k ], false, true )
                td = mw.html.create( "td" )
                            :node( e )
                tr:newline()
                  :node( td )
                td = mw.html.create( "td" )
                            :node( e2 )
                tr:newline()
                  :node( td )
                tbl:newline()
                   :node( tr )
            end -- for k, v
            r = tostring( tbl:newline() )
        end
    end
    return r
end -- Resolver.fire()



Text.fashion = function ( at, apply )
    -- Assign class and style
    -- Precondition:
    --     at     -- mw.html element
    --     apply  -- table, or not, may contain components class or style
    -- Postcondition:
    --     element modified
    if type( apply ) == "table" then
        local class = apply.class
        local css   = apply.style
        if class then
            if type( class ) == "string" then
                at:addClass( class )
            elseif type( class ) == "table" then
                for i = 1, #class do
                    at:addClass( class[ i ] )
                end -- for i
            end
        end
        if css then
            if type( css ) == "string" then
                at:cssText( css )
            elseif type( css ) == "table" then
                at:css( css )
            end
        end
    end
end -- Text.fashion()



Text.find = function ( available )
    -- Find best match of I18N options
    -- Precondition:
    --     available  -- table, I18N
    -- Postcondition:
    --     Returns
    --         1. string, with selected message, or not
    --         2. string, with language code, or not
    local r, r2
    local f = function ()
                  if type( r ) == "string" then
                      r = mw.text.trim( r )
                      if r == "" then
                          r = false
                      end
                  end
              end -- f()
    if type( available ) == "table" then
        if Text.Multilingual then
            r, r2 = Text.Multilingual.i18n( available,
                                            false,
                                            Config.frame )
        else
            Text.foreign()
            r = available[ Text.slang ]
            f()
            if r then
                r2 = Text.slang
            else
                r  = available.en
                r2 = "en"
            end
        end
    elseif type( available ) == "string" then
        r = available
    end
    f()
    if not r then
        for k, v in pairs( available ) do
            r = v
            f()
            if r then
                r2 = k
                break -- for k, v
            end
        end -- for k, v
    end
    f()
    if not r then
        r2 = false
    end
    return r, r2
end -- Text.find()



Text.flip = function ( about, apply )
    -- Retrieve specific module I18n text
    -- Precondition:
    --     about  -- string, with text ID
    --     apply  -- sequence table, with parameters
    -- Postcondition:
    --     Returns
    --         1. string, with selected message
    --         2. string, with language code
    local r, r2
    if type( Config.globals ) == "nil" then
        Text.flipper( WikidataScheme.suite )
    end
    if Config.globals then
        r = Config.globals[ about ]
    end
    if r then
        r, r2 = Text.find( r )
    end
    if not r then
        r = Config.defaults[ about ]
        if r then
            r2 = "en"
        else
            if type( about ) == "string" then
                r = string.format( "???I18N(%s)I18N???", about )
            else
                r = fault( "Invalid message ID type" )
            end
        end
    end
    if apply  and
       r:find( "$", 1, true )  and
       type( apply ) == "table" then
        r = mw.message.newRawMessage( r, apply ):plain()
    end
    return r, r2
end -- Text.flip()



Text.flipper = function ( apply )
    -- Retrieve global specific I18N translations
    -- Precondition:
    --     apply  -- string, with package ID
    -- Postcondition:
    local storage = string.format( "I18n/Module:%s.tab", apply )
    local lucky, t = pcall( mw.ext.data.get, storage, "_" )
    Config.globals = false
    if type( t ) == "table" then
        t = t.data
        if type( t ) == "table" then
            local e
            for i = 1, #t do
                e = t[ i ]
                if type( e ) == "table"  and
                   type( e[ 1 ] ) == "string"  and
                   type( e[ 2 ] ) == "table" then
                    Config.globals = Config.globals  or  { }
                    Config.globals[ e[ 1 ] ] = e[ 2 ]
                else
                    error( storage .. " has unexpected scheme",  0 )
                end
            end   -- for i
        else
            error( storage .. " corrupted",  0 )
        end
    end
end -- Text.flipper()



Text.flow = function ( at, alien, applied )
    -- Assign differing language and script direction
    -- Precondition:
    --     at       -- mw.html element
    --     alien    -- string, with language (or script) code, or not
    --     applied  -- string, with plain text, or not
    -- Postcondition:
    --     element modified if not matching
    --     Returns  true if script direction changed
    local dir = function ( a )
                    local r
                    if a then
                        r = "ltr"
                    else
                        r = "rtl"
                    end
                    return r
                end -- dir()
    local isRTL = function ( a )
                      local r
                      if Text.Multilingual then
                          r = Text.Multilingual.isRTL( a )
                      else
                          r = mw.language.new( a ):isRTL()
                      end
                      return r
                  end -- isRTL()
    local shift, slang
    if not Text.shift then
        Text.foreign( true )
        if Text.slang then
            Text.ltr = not isRTL( Text.slang )
        end
        if type( Text.ltr ) ~= "boolean" then
            Text.ltr = not mw.language.getContentLanguage():isRTL()
        end
        Text.shift = dir( Text.ltr )
    end
    if alien then
        Text.dirs = Text.dirs or { }
        shift     = Text.dirs[ alien ]
        if not shift then
            shift = dir( not isRTL( alien ) )
            Text.dirs[ alien ] = shift
        end
        if applied  and  shift == "rtl" then
            if not Text.Latn then
                Text.Latn = string.format( "^[ -%s]*$",
                                           mw.ustring.char( 0x052F ) )
            end
            if mw.ustring.find( applied, Text.Latn ) then
                shift = "ltr"
            end
        end
        if shift == Text.shift then
            shift = false
        end
        if alien ~= Text.slang then
            slang = alien
        end
    else
        shift = Text.shift
        slang = Text.slang
    end
    if shift then
        at:attr( "dir", shift )
    end
    if slang then
        at:attr( "lang", slang )
    end
    return  true and shift
end -- Text.flow()



Text.foreign = function ( again )
    -- Guess human language
    -- Precondition:
    --     again  -- true, for re-evaluation
    -- Postcondition:
    --     Returns  language code, or not
    if again  or  type( Text.slang ) == "nil" then
        if Text.Multilingual then
            Text.slang = Text.Multilingual.userLangCode()
        elseif not Text.slang then
            Text.slang = mw.language.getContentLanguage():getCode()
                                                         :lower()
        end
    end
    if Text.slang  and
       mw.ustring.codepoint( Text.slang, 1, 1 ) > 122 then
        Text.slang = false
    end
    return Text.slang
end -- Text.foreign()



Text.html = function ( available, as, alt, alien, across )
    -- Create HTML text element
    -- Precondition:
    --     available  -- table, I18N, string: plain text or ID, or not
    --     as         -- string, HTML tag name
    --     alt        -- string, with fallback text
    --     alien      -- string, with language (or script) code, or not
    --     across     -- number, of columns, for TD
    -- Postcondition:
    --     Returns  mw.html element
    local r = type( available )
    local show, slang
    if r == "table" then
        show, slang = Text.find( available )
    elseif r == "string" then
        if available:find( "^[QPL]%d+$" ) then
            local sign = available
            if mw.wikibase.isValidEntityId( sign ) then
                local s = Config.spaces[ sign:sub( 1, 1 ) ]
                if s then
                    sign = s .. sign
                else
                    sign = sign
                end
                show, slang = Text.wikibase( sign )
            else
                show = string.format( "%s %s",
                                        sign,
                                        Text.flip( "err_NoEntity" ) )
            end
        end
        if not show then
            show  = available
        end
        slang = slang or alien
    else
        show = alt
    end
    if show then
        r = mw.html.create( as )
        if slang then
            local bidi
            if as == "code" then
                r:attr( Config.coded )
                bidi = not Text.ltr
            else
                bidi = Text.flow( r, slang, show )
            end
            if bidi  and  as == "span" then
                r = mw.html.create( "bdo" )
                      :node( r )
                Text.flow( r, slang )
            end
        end
        r:wikitext( show )
        Text.fashion( r, available )
    else
        r = mw.html.create( "code" )
              :attr( Config.coded )
              :wikitext( as )
    end
    if across  and  across > 1 then
        r:attr( "colspan", tostring( across ) )
    end
    return r
end -- Text.html()



Text.templatedata = function ( area, about )
    -- Retrieve TemplateData system message
    -- Precondition:
    --     area   -- One of: "type", "desc", "status",
    --                       "example", "default"
    --     about  -- nil or in case of area=status: "required",
    --                                              "suggested",
    --                                              "optional",
    --                                              "deprecated"
    -- Postcondition:
    --     Returns  string, in general,
    --              mw.html span for area=example|default
    local s = "templatedata-doc-param-" .. area
    local r
    if about  and  area == "status" then
        s = string.format( "%s-%s", s, about )
    end
    Text.templatedataCache = Text.templatedataCache  or  { }
    r = Text.templatedataCache[ s ]
    if not r then
        local o = mw.message.new( s )
        if o:exists() then
            if Text.foreign( true ) then
                o:inLanguage( Text.slang )
            end
            r = o:plain()
        else
            r = string.format( "???(%s)???", s )
        end
        if area == "example"  or  area == "default" then
            r = mw.html.create( "span" )
                       :addClass( string.format( "%s-label-%s",
                                                 WikidataScheme.suite,
                                                 area ) )
                       :wikitext( r )
        end
        Text.templatedataCache[ s ] = r
    end
    return r
end -- Text.templatedata()



Text.wikibase = function ( all, about, attempt )
    -- Translation of wikibase component
    -- Precondition:
    --     all      -- string or table, object ID or entity
    --     about    -- boolean, true "descriptions" or false "labels"
    --     attempt  -- string or not, code of preferred language
    -- Postcondition:
    --     Returns
    --         1. string, with selected message, or not
    --         2. string, with language code, or not
    local r, r2
    if Text.Multilingual then
        r, r2 = Text.Multilingual.wikibase( all,
                                            about,
                                            attempt,
                                            Config.frame )
    else
        local s = type( all )
        local object
        if s == "table" then
            object = all
        elseif s == "string" then
            object = mw.wikibase.getEntity( all )
            r      = all
        end
        if type( object ) == "table" then
            if about then
                s = "descriptions"
            else
                s = "labels"
            end
            object = object[ s ]
            if type( object ) == "table" then
                if object[ attempt ] then
                    r  = object[ attempt ].value
                    r2 = attempt
                elseif object.en then
                    r  = object.en.value
                    r2 = "en"
                else
                    local poly
                    for k, v in pairs( object ) do
                        r  = v.value
                        r2 = k
                        break -- for k, v
                    end -- for k, v
                end
            end
        end
    end
    return r or "????????", r2
end -- Text.wikibase()



Table.features = function ( assigned, across, already, asked, above )
    -- Describe entry features
    -- Precondition:
    --     assigned  -- table, with definition of one entry
    --     across    -- number, of columns, or nil on second level
    --     already   -- true, if first element to be created as TR
    --     asked     -- true, if first element to be created as TR
    --     above     -- string, with context property ID
    -- Postcondition:
    --     Returns  sequence table with mw.html objects
    --         1. table, with TD
    --         2. table, with TR, or not
    local r1, r2, td
    local f = function ( add, also, a )
                  if add then
                      if across  and  across > 1  and  not a then
                          add:attr( "colspan", tostring( across ) )
                      end
                      if already or r1 then
                          local tr
                          if add or not r2 then
                              tr = mw.html.create( "tr" )
                                          :node( add )
                              Table.flag( tr, asked )
                              if also then
                                  tr:newline()
                                    :node( also )
                              end
                              r2 = r2  or  { }
                              table.insert( r2, tr )
                          else
                              tr = r2[ #r2 ]
                          end
                      else
                          r1 = { }
                          table.insert( r1, add )
                          if also then
                              table.insert( r1, also )
                          end
                      end
                  end
              end -- f()
    local f1 = function ( already, add, about )
                   local ttd = Text.templatedata( about )
                   local sel = string.format( "%s-%s",
                                              WikidataScheme.suite,
                                              about )
                   local r   = already
                   local lift
                   if r then
                       r:newline()
                   else
                       r    = mw.html.create( "td" )
                       lift = true
                   end
                   r:node( mw.html.create( "div" )
                                  :node( ttd ) )
                    :newline()
                    :node( add:addClass( sel ) )
                   if lift then
                       f( r )
                   end
                   return r
               end -- f1()
    local f2 = function ( add, about )
                   local ttd = Text.templatedata( about )
                   local sel = string.format( "%s-%s",
                                              WikidataScheme.suite,
                                              about )
                   local r   = already
                   f( mw.html.create( "td" )
                             :node( ttd ),
                      add:addClass( sel ),
                      true )
               end -- f2()
    if type( assigned ) == "table" then
        f( Table.field( assigned, "intro", 2 ) )
        if type( assigned.qlist ) == "table" then
            td = mw.html.create( "td" )
            if type( assigned.q ) == "nil" then
                local ul = Table.filled( assigned.qlist )
                if ul then
                    f( td:node( ul ) )
                end
            else
                f(td:wikitext( fault( Text.flip( "err_q_qlist" ) ) ) )
            end
        elseif type( assigned.q ) ~= "nil" then
            td = mw.html.create( "td" )
            f( td:node( Table.fixed( assigned, "q" ) ) )
        end
        if type( assigned.qdefault ) ~= "nil" then
            local q = Table.fixed( assigned, "qdefault" )
            if across == 2 then
                q = mw.html.create( "td" )
                           :node( q )
                f2( q, "default" )
            else
                td = f1( td, q, "default" )
            end
        end
        if type( assigned.example ) ~= "nil" then
            if across == 2 then
                local q = Table.field( assigned, "example" )
                f2( q, "example" )
            else
                local q = Text.html( assigned.example, "div" )
                td = f1( td, q, "example" )
            end
        elseif type( assigned.qqexample ) == "table" then
            local qpq = Table.fruit( above, assigned.qqexample )
            if across == 2 then
                f2( mw.html.create( "td" )
                           :node( qpq ),
                    "example" )
            else
                td = f1( td, qpq, "example" )
            end
        end
        if type( assigned.qualifiers ) == "table" then
            if across == 2 then
                local hd, q, rows
                for i = 1, #assigned.qualifiers do
                    q = assigned.qualifiers[ i ]
                    if type( q ) == "table" then
                        hd, rows = Table.further( q, hd )
                        if hd then
                            f( hd[ 1 ], hd[ 2 ], true )
                        end
                        if rows then
                            r2 = r2  or  { }
                            for k = 1, #rows do
                                table.insert( r2, rows[ k ] )
                            end -- for k
                        end
                    else
                        q = Text.flip( "err_BadQualifier" )
                        f( mw.html.create( "td" )
                             :wikitext( fault( q ) ) )
                    end
                end -- for i
            else
                td = mw.html.create( "td" )
                       :wikitext( fault( Text.flip( "err_Nesting" ) ) )
                f( td )
            end
        end
        f( Table.field( assigned, "terminate", 2 ) )
    end
    if not ( r1 ) then
        f( mw.html.create( "td" ) )
    end
    return r1, r2
end -- Table.features()



Table.fetch = function ( access, alike, atom, alone )
    -- Describe registration entity
    -- Precondition:
    --     access  -- string, with entity name
    --     alike   -- string, if Property or Item required, "P" or "Q"
    --     atom    -- true, if two elements required
    --     alone   -- true, if combined linked element required
    -- Postcondition:
    --     Returns
    --         1. string, with entity ID and label, or error message,
    --            or mw.html code element for atom
    --         2. mw.html span element for atom
    local sign, r1 = Resolver.fetch( access, alike )
    local r2
    if sign then
        local s = Config.spaces[ sign:sub( 1, 1 ) ]
        local say, slang
        if s then
            s = s .. sign
        else
            s = sign
        end
        say, slang = Text.wikibase( sign, alike )
        r2 = Text.html( say, "span", false, slang )
        if alone then
            r1 = string.format( "[[d:%s|%s]]", s, tostring( r2 ) )
        else
            s  = string.format( "[[d:%s|%s]]", s, sign )
            r1 = Text.html( s, "code", false, "en" )
            if not atom then
                r1 = string.format( "%s %s",
                                    tostring( r1 ),
                                    tostring( r2 ) )
            end
        end
    elseif r1 then
        r1 = fault( r1 )
    else
        r1 = fault( "Table.fetch()" )
    end
    return r1, r2
end -- Table.fetch()



Table.fiat = function ( assign )
    -- Describe registration claim as major table row
    -- Precondition:
    --     assign  -- table, with definition of one entry
    -- Postcondition:
    --     Returns  sequence table, with mw.html.TR objects
    local tr = mw.html.create( "tr" )
    local td = mw.html.create( "td" )
    local r  = { }
    if type( assign ) == "table" then
        local state = Table.flag( tr, assign.state )
        local how, n, rows, v
        Text.fashion( tr, assign )
        tr:addClass( string.format( "%s-%s",
                                    WikidataScheme.suite, state ) )
        v = Table.fetch( assign.subject )
        td:wikitext( v )
        if type( assign.values ) == "table" then
            for i = 1, #assign.values do
                how, v = Table.features( assign.values[ i ],
                                         2,
                                         how,
                                         state,
                                         assign.subject )
                if v then
                    rows = rows or { }
                    for i = 1, #v do
                        table.insert( rows, v[ i ] )
                    end -- for i
                end
            end -- for i
        else
            how, rows = Table.features( false,
                                        2,
                                        false,
                                        state,
                                        assign.subject )
            --[==[ if not ( how or rows ) then
                td = mw.html.create( "td" )
                       :wikitext( fault( Text.flip( "err_Unguided" ) ) )
                f( td )
            end ]==]
        end
        if rows then
            n = #rows + 1
        else
            n = 0
        end
        if n > 1 then
            td:attr( "rowspan", tostring( n ) )
        end
        tr:newline()
          :node( td )
        if how then
            for i = 1, #how do
                tr:newline()
                  :node( how[ i ] )
            end -- for i
        end
        td = mw.html.create( "td" )
               :wikitext( Text.templatedata( "status", state ) )
        if n > 1 then
            td:attr( "rowspan", tostring( n ) )
        end
        tr:newline()
          :node( td )
        table.insert( r, tr )
        for i = 1, n do
            table.insert( r, rows[ i ] )
        end -- for i
    else
        td:attr( "colspan", "3" )
          :css( { ["background-color"] = "#FFFF00" } )
          :wikitext( fault( Text.flip( "err_InvalidClaim" ) ) )
        tr:node( td )
        table.insert( r, tr )
    end
    return r
end -- Table.fiat()



Table.field = function ( all, ask, across )
    -- Insert plain text table cell
    -- Precondition:
    --     all     -- table, with request
    --     ask     -- string, with key in all
    --     across  -- number, of columns, or not
    -- Postcondition:
    --     Returns  mw.html.TD object, or nothing
    local q = all[ ask ]
    local r
    if q then
        local n = across or 1
        r = Text.html( q, "td", false, false, n )
    end
    return r
end -- Table.field()



Table.filled = function ( all )
    -- Create unordered list of items
    -- Precondition:
    --     all  -- sequence table, with entity names, and true as last
    -- Postcondition:
    --     Returns  mw.html.UL, or nothing
    local q, r, s
    for i = 1, #all do
        r = r  or  mw.html.create( "ul" )
        q = all[ i ]
        if type( q ) == "number" then
            s = tostring( q )
            if s:find( "^[1-9]%d*$" ) then
                q = "Q" .. s
            end
        end
        if type( q ) == "string" then
            s = Table.fetch( q )
        elseif i == #all  and  q == true then
            s = "&hellip;"
        else
            q = mw.html.create( "code" )
                  :wikitext( tostring( q ) )
            s = Text.flip( "err_InvalidNameType" )
            s = string.format( "%s %s",  tostring( q ),  fault( s ) )
        end
        r:newline()
         :node( mw.html.create( "li" )
                  :wikitext( s ) )
    end -- for i
    r:newline()
    return r
end -- Table.filled()



Table.fixed = function ( assigned, at )
    -- Create mandatory/default item (Q)
    -- Precondition:
    --     assigned  -- string or number or sequence table of strings,
    --               with item identifier
    --     at     -- string, with ID within assign, "q" or "qdefault"
    --               -> string or number or sequence table of strings,
    --               with item identifier
    -- Postcondition:
    --     Returns  mw.html.span
    local v = assigned[ at ]
    local s = type( v )
    local r
    if s == "string" then
        s = v
    elseif s == "number" then
        if v > 0  and
           v == math.floor( v ) then
            s = string.format( "Q%d", v )
        else
            s = false
        end
    elseif s == "table" then
        local n = #v
        if n == 1 then
            r = Table.fixed( v[ 1 ], at )
        elseif n > 1 then
            r = mw.html.create( "span" )
            for i = 1, #n do
                if i > 1 then
                    r:node( mw.html.create( "br" ) )
                     :newline()
                end
                r:node( Table.fixed( v[ i ], at ) )
            end -- for i
        else
            s = false
        end
    else
        s = false
    end
    if not r then
        if s then
            s = Table.fetch( s )
        else
            s = fault( Text.flip( "err_NoEntity" ) )
        end
        r = mw.html.create( "span" )
                   :wikitext( s )
    end
    return r
end -- Table.fixed()



Table.flag = function ( adjust, assign )
    -- Equip element with state style
    -- Precondition:
    --     adjust  -- mw.html object, to be flagged
    --     assign  -- state name
    -- Postcondition:
    --     element modified
    --     Returns defined state name
    local r = assign
    local css
    if Config.css[ r ] then
        css = Config.css[ r ]
    else
        r   = "optional"
        css = { ["background-color"] = "#FFFF00" }
    end
    adjust:css( css )
    css = Config.feature( "css", r, "table string" )
    if type( css ) == "string" then
        adjust:cssText( css )
    elseif type( css ) == "table" then
        adjust:css( css )
    end
    return r
end -- Table.flag()



Table.flat = function ( all, ask, across, append )
    -- Insert plain text table row
    -- Precondition:
    --     all     -- table, with request
    --     ask     -- string, with key in all
    --     across  -- number, of columns, or not
    --     append  -- mw.html object, to be added to, or nothing
    -- Postcondition:
    --     Returns  mw.html.TR object, or nothing
    local q = all[ ask ]
    local r
    if q then
        local n = across or 1
        local s = Config.field( ask )
        local td
        r = mw.html.create( "tr" )
        if s then
            td = mw.html.create( "td" )
                   :wikitext( s )
            r:newline()
             :node( td )
            n = 2
        end
        td = Text.html( q, "td", false, false, n )
        r:newline()
         :node( td )
        if append then
            append:newline()
                  :node( r )
        end
    end
    return r
end -- Table.flat()



Table.form = function ( all )
    -- Create <table> for registration elements
    -- Precondition:
    --     all  -- table, with entire request
    -- Postcondition:
    --     Returns  string with entire HTML table
    local tbl = mw.html.create( "table" )
                  :addClass( "wikitable" )
                  :addClass( WikidataScheme.suite .. "-table" )
    local tr, rows
    if type( all.claims ) == "table" then
        local got
        for i = 1, #all.claims do
            got = Table.fiat( all.claims[ i ] )
            for k = 1, #got do
                rows = rows or { }
                table.insert( rows, got[ k ] )
            end -- for k
        end -- for i
    end
    if not rows then
        rows = { Table.fiat( false ) }
    end
    if all.caption then
        local o = type( all.caption )
        if o == "string"  or  o == "table" then
            o = Text.html( all.caption, "caption" )
            Text.fashion( caption, all.caption )
            tbl:newline()
               :node( o )
        end
    end
    Table.flat( all, "onLabel", 2, tbl )
    Table.flat( all, "onDesc",  2, tbl )
    Table.flat( all, "onAlias", 2, tbl )
    Text.shift = false
    Text.flow( tbl )
    Text.fashion( tbl, all )
    if type( all.id ) == "string" then
        tbl:attr( "id", all.id )
    end
    tr = mw.html.create( "tr" )
    tr:newline()
      :node( mw.html.create( "th" )
                    :attr( "title", "type" )
                    :css( Config.css.tablehead )
                    :wikitext( Text.templatedata( "type" ) ) )
      :newline()
      :node( mw.html.create( "th" )
                    :attr( "colspan", "2" )
                    :css( Config.css.tablehead )
                    :wikitext( Text.templatedata( "desc" ) ) )
      :newline()
      :node( mw.html.create( "th" )
                    :attr( "title", "status" )
                    :css( Config.css.tablehead )
                    :wikitext( Text.templatedata( "status" ) ) )
    tbl:newline()
--     :node( mw.html.create( "thead" )
                     :node( tr )
--          )
    for i = 1, #rows do
        tbl:newline()
           :node( rows[ i ] )
    end -- for i
    if all.footer then
        local o = type( all.footer )
        if o == "string"  or  o == "table" then
            o = Table.flat( all, "footer", 3 )
            Text.fashion( footer, all.footer )
            tbl:newline()
               :node( o )
        end
    end
    return  tostring( tbl:newline() )
end -- Table.form()



Table.fruit = function ( assigned, adjoin )
    -- Return content of a qqexample chain
    -- Precondition:
    --     assigned   -- string, with name of P
    --     adjoin     -- table, with 2 elements as Q names: Item, P-value
    -- Postcondition:
    local e, r, s
    if type( adjoin ) == "table"  and  #adjoin == 2 then
        local p
        p, e = Resolver.fetch( assigned, "P" )
        if p then
            local q1
            q1, e = Resolver.fetch( adjoin[ 1 ], "Q" )
            if q1 then
                local q2
                q2, e = Resolver.fetch( adjoin[ 2 ], "Q" )
                if q2 then
                    local x
                    r = mw.html.create( "span" )
                    x, p = Table.fetch( p, false, true )
                    q1   = Table.fetch( q1, false, false, true )
                    q2   = Table.fetch( q2, false, false, true )
                    r:wikitext( q1 )
                     :wikitext( " &lt;" )
                     :node( p )
                     :wikitext( "&gt; " )
                     :wikitext( q2 )
                end
            end
        end
    end
    if not r then
        e = Text.flip( "err_BadExample" )
    end
    if e then
        r = fault( e )
    end
    return r
end -- Table.fruit()



Table.further = function ( assign, already )
    -- Describe one qualifier as middle column table row
    -- Precondition:
    --     assign   -- table, with definition of one qualifier
    --     already  -- true, if first element to be created as TR
    -- Postcondition:
    --     Returns sequence tables with mw.html objects, or not
    --         1. TD, if not already
    --         2. TR, if any
    local state, r1, r2, tr
    local f = function ( add, also )
                  if add then
                      if state then
                          Table.flag( add, state )
                          if also then
                              Table.flag( also, state )
                          end
                      end
                      if already or r1 then
                          tr = mw.html.create( "tr" )
                                 :newline()
                                 :node( add )
                          if also then
                              tr:newline()
                                :node( also )
                          end
                          r2 = r2  or  { }
                          table.insert( r2, tr )
                      else
                          r1 = { }
                          table.insert( r1, add )
                          if also then
                              table.insert( r1, also )
                          end
                      end
                  end
              end -- f()
    local td
    local how, rows
    if Config.statelist[ assign.state ] then
        state = assign.state
    else
        state = "optional"
    end
    f( Table.flat( assign, "intro", 2 ) )
    how, rows = Table.features( assign, 1, r1, state, assign.subject )
    r1 = r1 or how
    if rows then
        r2 = r2  or  { }
        for i = 1, #rows do
            table.insert( r2, rows[ i ] )
        end -- for i
    end
    f( Table.flat( assign, "terminate" ) )
    td = mw.html.create( "td" )
           :node( Table.fetch( assign.subject, "P" ) )
    if r1 then
        table.insert( r1, 1, td )
    elseif r2 then
        r2[ 1 ]:newline()
               :node( td )
    else
        f( td )
    end
    return r1, r2
end -- Table.further()



WikidataScheme.fetch = function ( about )
    -- Retrieve Lua table
    -- Precondition:
    --     about  -- table or JSON string or mw.loadData page name
    -- Postcondition:
    --     Returns  string with error message, or not
    --     Makes Lua table available as .Request
    local s = type( about )
    local r
    if s == "string" then
        local lucky, d
        s = mw.text.trim( about )
        if s:sub( 1, 1 ) == "{"  and
           s:sub( -1 ) == "}" then
            lucky, d = pcall( mw.text.jsonDecode, about )
        elseif not s:find( "\n", 2, true ) then
            lucky, d = pcall( mw.loadData, about )
        end
        s = type( d )
        if s == "table" then
            WikidataScheme.Request = d
        elseif s == "string" then
            r = d
        else
            r = "Invalid data for WikidataScheme"
        end
    elseif s == "table" then
        WikidataScheme.Request = about
    else
        r = "Invalid request for WikidataScheme"
    end
    return r
end -- WikidataScheme.fetch()



WikidataScheme.flat = function ( about, adjust, frame )
    -- Export registration description as JSON
    -- Precondition:
    --     about   -- table or JSON string or mw.loadData page name
    --     adjust  -- true, for resolving symbolic IDs
    --     frame   -- frame, if available
    -- Postcondition:
    --     Returns  string with JSON or error message
    local r = WikidataScheme.fetch( about )
    if not r then
        Config.frame = Config.frame  or  frame  or  mw.getCurrentFrame()
        Config.first()
        r = JSONexport.full( WikidataScheme.Request, adjust )
        r = failures() .. r
    end
    return r
end -- WikidataScheme.flat()



WikidataScheme.form = function ( about, assigned, frame )
    -- Describe registration elements
    -- Precondition:
    --     about     -- table or JSON string or mw.loadData page name
    --     assigned  -- true, for resolve table only
    --     frame     -- frame, if available
    -- Postcondition:
    --     Returns  string with entire HTML table
    local r
    Config.frame = Config.frame  or  frame  or  mw.getCurrentFrame()
    if not Text.Multilingual  and  Text.Multilingual ~= false then
        local bib = foreignModule( "Multilingual",
                                   true,
                                   false,
                                   WikidataScheme.globals.Multilingual,
                                   true )
        if type( bib ) == "table"  and
           type( bib.Multilingual ) == "function" then
            Text.Multilingual = bib.Multilingual()
        else
            Text.Multilingual = false
        end
    end
    r = WikidataScheme.fetch( about )
    if not r then
        Config.first()
        if assigned then
            r = Resolver.fire( WikidataScheme.Request )
        else
            r = Table.form( WikidataScheme.Request )
        end
    end
    r = failures() .. r
    return r
end -- WikidataScheme.form()



Failsafe.failsafe = function ( atleast )
    -- Retrieve versioning and check for compliance
    -- Precondition:
    --     atleast  -- string, with required version or "wikidata" or "~"
    --                 or false
    -- Postcondition:
    --     Returns  string  -- with queried version, also if problem
    --              false   -- if appropriate
    -- 2019-10-15
    local last  = ( atleast == "~" )
    local since = atleast
    local r
    if last  or  since == "wikidata" then
        local item = Failsafe.item
        since = false
        if type( item ) == "number"  and  item > 0 then
            local entity = mw.wikibase.getEntity( string.format( "Q%d",
                                                                 item ) )
            if type( entity ) == "table" then
                local 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
                    else
                        r = vsn.value
                    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()



-- Export
local p = { }

p.form = function ( frame )
    local s = mw.text.trim( frame.args[ 1 ]  or  "" )
    local r
    if s ~= "" then
        r = WikidataScheme.form( s, false, frame )
    end
    return r or ""
end -- p.form

p.format = function ( frame )
    local s = mw.text.trim( frame.args[ 1 ]  or  "" )
    local r
    if s ~= "" then
        r = WikidataScheme.flat( s, false, frame )
    end
    return r or ""
end -- p.form

p.furnished = function ( frame )
    local s = mw.text.trim( frame.args[ 1 ]  or  "" )
    local r
    if s ~= "" then
        r = WikidataScheme.form( s, true, frame )
    end
    return r or ""
end -- p.furnished

p.JSON = function ( frame )
    local s = mw.text.trim( frame.args[ 1 ]  or  "" )
    local r
    if s ~= "" then
        r = WikidataScheme.flat( s, true, frame )
    end
    return r or ""
end -- p.JSON

p.failsafe = function ( frame )
    -- Versioning interface
    local s = type( frame )
    local since
    if s == "table" then
        since = frame.args[ 1 ]
    elseif s == "string" then
        since = frame
    end
    if since then
        since = mw.text.trim( since )
        if since == "" then
            since = false
        end
    end
    return Failsafe.failsafe( since )  or  ""
end -- p.failsafe()

p.WikidataScheme = function ()
    WikidataScheme.Text = Text
    return WikidataScheme
end -- p.WikidataScheme

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.