Modul:WikidataScheme
Erscheinungsbild
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.
Wenn die serial-Information nicht übereinstimmt, müsste eine Kopie hiervon in das lokale Wiki geschrieben werden.
Versionsbezeichnung auf WikiData:
2019-12-16
Updating notwendig
(lokal:2019-12-04
)local WikidataScheme = { suite = "WikidataScheme",
serial = "2019-12-04",
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 Table = { }
local Text = { }
Config.colors = { tableheadbg = "B3B7FF",
required = "EAF3FF",
suggested = "FFFFFF",
optional = "EAECF0",
deprecated = "FFCBCB" }
Config.spaces = { L = "Lexeme:",
P = "Property:",
Q = false }
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_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_NoGuidance = "Missing guidance",
err_NoNameResolver = "needs",
err_NoProperty = "is not a Property" }
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 fetch = function ( access, alike )
-- Resolve entity name
-- Precondition:
-- access -- string, with entity name or ID
-- alike -- true, if Property required
-- 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 ) ~= "P" then
r2 = string.format( "%s %s",
r1,
Text.flip( "err_NoProperty" ) )
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 -- fetch()
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.qlist then
Config.css = { }
Config.qlist = { }
for k, v in pairs( Config.colors ) do
if k == "tableheadbg" then
k = "tablehead"
else
Config.qlist = k
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 types = { "P", "Q", "L" }
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, #types do
s = types[ i ]
j = v[ s ]
if type( j ) == "number" and
j >= 0 and
j == math.floor( j ) then
coll = coll or { }
k = k:gsub( "\\", "\\\\" )
:gsub( "\"", "\\\"" )
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.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 -- true, if Property required
-- Postcondition:
local r
if type( access ) == "string" then
local s = mw.text.trim( access )
if s ~= "" then
if adjust then
r = 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 assign.state == "required" or
assign.state == "suggested" 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.flat( assign, "example", indent ) )
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, "terminate", 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.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 s = type( e )
local r, shift, t
if s == "table" then
for k, v in pairs( e ) do
local parts
if type( k ) == "string" and
type( v ) == "string" and
k:find( "^%l%l" ) 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( "^%u%u$" ) then
k = false
end
else
k = false
end
if k then
t = t or { }
t[ k ] = v
end
end
end -- for k, v
elseif s == "string" then
if e:match( "^[PQL]%d+$" ) then
r = string.format( "\"%s\"", e )
else
t = { }
t.und = e
end
end
if r or t then
shift = string.rep( " ", align )
end
if t then
local sep = ""
r = "{\n"
for k, v in pairs( t ) do
v = v:gsub( "\\", "\\\\" )
:gsub( "\"", "\\\"" )
r = string.format( "%s%s%s \"%s\": \"%s\"",
r, sep, shift, k, v )
sep = ",\n"
end -- for k, v
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()
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
r = r .. "\n}"
return r
end -- JSONexport.full()
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( "dir", "ltr" )
bidi = not Text.ltr
else
bidi = Text.flow( r, slang, show )
end
if bidi and
( as == "span" or as == "code" ) 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( "dir", "ltr" )
: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"
-- about -- nil or one of: "required", "suggested", "optional"
-- Postcondition:
-- Returns string
local s = "templatedata-doc-param-" .. area
local r
if about 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
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 )
-- 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
-- Postcondition:
-- Returns sequence table with mw.html objects
-- 1. table, with TD
-- 2. table, with TR, or not
local r1, r2
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 = mw.html.create( "tr" )
: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
if type( assigned ) == "table" then
f( Table.field( assigned, "intro", 2 ) )
if type( assigned.qlist ) == "table" then
local ul = Table.filled( assigned.qlist )
if ul then
f( mw.html.create( "td" )
:node( ul ) )
end
end
if type( assigned.qualifiers ) == "table" then
if across 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 or r2 ) then
td = mw.html.create( "td" )
:wikitext( fault( Text.flip( "err_NoGuidance" ) ) )
f( td )
end
return r1, r2
end -- Table.features()
Table.fetch = function ( access, alike )
-- Describe registration entity
-- Precondition:
-- access -- string, with entity name
-- alike -- true, if Property required
-- Postcondition:
-- Returns string, with entity ID and label, or error message
local sign, r = fetch( access, alike )
if sign then
local s = Config.spaces[ sign:sub( 1, 1 ) ]
local e1, e2, slang
if s then
s = s .. sign
else
s = sign
end
s = string.format( "[[d:%s|%s]]", s, sign )
e1 = Text.html( s, "code", false, "en" )
s, slang = Text.wikibase( sign, alike )
e2 = Text.html( s, "span", false, slang )
r = string.format( "%s %s",
tostring( e1 ),
tostring( e2 ) )
elseif r then
r = fault( r )
else
r = fault( "Table.fetch()" )
end
return r
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
Text.fashion( tr, assign )
tr:addClass( string.format( "%s-%s",
WikidataScheme.suite, state ) )
td:wikitext( Table.fetch( assign.subject ) )
if type( assign.values ) == "table" then
local v
for i = 1, #assign.values do
how, v = Table.features( assign.values[ i ], 2, how )
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 )
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, false )
elseif i == #all and q == true then
s = "…"
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.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.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 assign.state == "required" or assign.state == "suggested" then
state = assign.state
end
f( Table.flat( assign, "intro", 2 ) )
how, rows = Table.features( assign, 1, r1 )
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" )
:wikitext( Table.fetch( assign.subject, true ) )
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()
r = JSONexport.full( WikidataScheme.Request, adjust )
r = failures() .. r
end
return r
end -- WikidataScheme.flat()
WikidataScheme.form = function ( about, frame )
-- Describe registration elements
-- Precondition:
-- about -- table or JSON string or mw.loadData page name
-- 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()
r = Table.form( WikidataScheme.Request )
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, 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.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