Modul:TemplateData: Unterschied zwischen den Versionen
te>PerfektesChaos (2017-03-28) |
K (64 Versionen importiert) |
||
(61 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
− | local TemplateData = { serial = " | + | local TemplateData = { suite = "TemplateData", |
− | + | serial = "2019-07-02", | |
+ | item = 46997995 } | ||
--[=[ | --[=[ | ||
improve template:TemplateData | improve template:TemplateData | ||
Zeile 8: | Zeile 9: | ||
local Config = { | local Config = { | ||
− | -- multiple | + | -- multiple option names mapped into unique internal fields |
− | + | basicCnf = { catProblem = "strange", | |
− | + | classNoNumTOC = "suppressTOCnum", | |
− | + | cssParWrap = "cssTabWrap", | |
− | + | cssParams = "cssTable", | |
− | + | docpageCreate = "suffix", | |
− | loudly | + | docpageDetect = "subpage", |
− | solo | + | helpBoolean = "support4boolean", |
− | strange | + | helpContent = "support4content", |
− | subpage | + | helpDate = "support4date", |
− | suffix | + | helpFile = "support4wiki-file-name", |
+ | helpFormat = "supportFormat", | ||
+ | helpLine = "support4line", | ||
+ | helpNumber = "support4number", | ||
+ | helpPage = "support4wiki-page-name", | ||
+ | helpString = "support4string", | ||
+ | helpTemplate = "support4wiki-template-name", | ||
+ | helpURL = "support4url", | ||
+ | helpUser = "support4wiki-user-name", | ||
+ | msgDescMiss = "solo", | ||
+ | tStylesTOCnum = "stylesTOCnum" }, | ||
+ | -- classParams = "classTable", | ||
+ | -- classTable = false, -- class for params table | ||
+ | debugmultilang = "C0C0C0", | ||
+ | loudly = false, -- show exported element, etc. | ||
+ | solo = false, -- complaint on missing description | ||
+ | strange = false, -- title of maintenance category | ||
+ | cssTable = false, -- styles for params table | ||
+ | cssTabWrap = false, -- styles for params table wrapper | ||
+ | debug = false, | ||
+ | subpage = false, -- pattern to identify subpage | ||
+ | suffix = false, -- subpage creation scheme | ||
+ | suppressTOCnum = false, -- class for TOC number suppression | ||
+ | jsonDebug = "json-code-lint" -- class for jsonDebug tool | ||
} | } | ||
local Data = { | local Data = { | ||
Zeile 26: | Zeile 50: | ||
less = false, -- main description missing | less = false, -- main description missing | ||
lasting = false, -- old syntax encountered | lasting = false, -- old syntax encountered | ||
+ | lazy = false, -- doc mode; do not generate effective <templatedata> | ||
+ | leading = false, -- show TOC | ||
-- low = false, -- 1= mode | -- low = false, -- 1= mode | ||
order = false, -- parameter sequence | order = false, -- parameter sequence | ||
params = false, -- table, exported parameters | params = false, -- table, exported parameters | ||
scream = false, -- error messages | scream = false, -- error messages | ||
− | slang = | + | sibling = false, -- TOC juxtaposed |
+ | slang = nil, -- project/user language code | ||
slim = false, -- JSON reduced to plain | slim = false, -- JSON reduced to plain | ||
source = false, -- JSON input | source = false, -- JSON input | ||
Zeile 39: | Zeile 66: | ||
} | } | ||
local Permit = { | local Permit = { | ||
− | colors = { required | + | builder = { after = "block", |
− | suggested | + | align = "block", |
− | optional | + | block = "block", |
− | deprecated = "FFCBCB" }, | + | compressed = "block", |
+ | dense = "block", | ||
+ | grouped = "inline", | ||
+ | half = "inline", | ||
+ | indent = "block", | ||
+ | inline = "inline", | ||
+ | last = "block", | ||
+ | lead = "block", | ||
+ | newlines = "*", | ||
+ | spaced = "inline" }, | ||
+ | colors = { tableheadbg = "B3B7FF", | ||
+ | required = "EAF3FF", | ||
+ | suggested = "FFFFFF", | ||
+ | optional = "EAECF0", | ||
+ | deprecated = "FFCBCB" }, | ||
params = { aliases = "table", | params = { aliases = "table", | ||
autovalue = "string", | autovalue = "string", | ||
− | default = "string table I18N", | + | default = "string table I18N nowiki", |
− | deprecated = "boolean string", | + | deprecated = "boolean string I18N", |
description = "string table I18N", | description = "string table I18N", | ||
− | example = "string table I18N", | + | example = "string table I18N nowiki", |
label = "string table I18N", | label = "string table I18N", | ||
inherits = "string", | inherits = "string", | ||
required = "boolean", | required = "boolean", | ||
+ | style = "string table", | ||
suggested = "boolean", | suggested = "boolean", | ||
type = "string" }, | type = "string" }, | ||
Zeile 86: | Zeile 128: | ||
-- alert -- string, error message | -- alert -- string, error message | ||
if Data.scream then | if Data.scream then | ||
− | string.format( "%s *** %s", Data.scream, alert ) | + | Data.scream = string.format( "%s *** %s", Data.scream, alert ) |
else | else | ||
Data.scream = alert | Data.scream = alert | ||
end | end | ||
end -- Fault() | end -- Fault() | ||
+ | |||
+ | |||
+ | |||
+ | local function Fetch( ask, allow ) | ||
+ | -- Fetch module | ||
+ | -- Parameter: | ||
+ | -- ask -- string, with name | ||
+ | -- "/global" | ||
+ | -- "JSONutil" | ||
+ | -- "Multilingual" | ||
+ | -- "Text" | ||
+ | -- "WLink" | ||
+ | -- allow -- true: no error if unavailable | ||
+ | -- Returns table of module | ||
+ | -- error: Module not available | ||
+ | local sign = ask | ||
+ | local r, stem | ||
+ | if sign:sub( 1, 1 ) == "/" then | ||
+ | sign = TemplateData.frame:getTitle() .. sign | ||
+ | else | ||
+ | stem = sign | ||
+ | sign = "Module:" .. stem | ||
+ | end | ||
+ | if TemplateData.extern then | ||
+ | r = TemplateData.extern[ sign ] | ||
+ | else | ||
+ | TemplateData.extern = { } | ||
+ | end | ||
+ | if not r then | ||
+ | local lucky, g = pcall( require, sign ) | ||
+ | if type( g ) == "table" then | ||
+ | if stem and type( g[ stem ] ) == "function" then | ||
+ | r = g[ stem ]() | ||
+ | else | ||
+ | r = g | ||
+ | end | ||
+ | TemplateData.extern[ sign ] = r | ||
+ | elseif not allow then | ||
+ | error( string.format( "Fetch(%s) %s", sign, g ) ) | ||
+ | end | ||
+ | end | ||
+ | return r | ||
+ | end -- Fetch() | ||
+ | |||
+ | |||
+ | |||
+ | local function Foreign() | ||
+ | -- Guess human language | ||
+ | -- Returns slang, or not | ||
+ | if type( Data.slang ) == "nil" then | ||
+ | local Multilingual = Fetch( "Multilingual", true ) | ||
+ | if Multilingual and | ||
+ | type( Multilingual.userLangCode ) == "function" then | ||
+ | Data.slang = Multilingual.userLangCode() | ||
+ | else | ||
+ | Data.slang = mw.language.getContentLanguage():getCode() | ||
+ | :lower() | ||
+ | end | ||
+ | end | ||
+ | if Data.slang and | ||
+ | mw.ustring.codepoint( Data.slang, 1, 1 ) > 122 then | ||
+ | Data.slang = false | ||
+ | end | ||
+ | return Data.slang | ||
+ | end -- Foreign() | ||
Zeile 102: | Zeile 209: | ||
local seek = string.format( Permit.search, | local seek = string.format( Permit.search, | ||
ask:gsub( "%%", "%%%%" ) | ask:gsub( "%%", "%%%%" ) | ||
− | :gsub( "%-", "%%% | + | :gsub( "([%-.()+*?^$%[%]])", |
+ | "%%%1" ) ) | ||
local i, k = Data.source:find( seek, at ) | local i, k = Data.source:find( seek, at ) | ||
local r, slice, source | local r, slice, source | ||
Zeile 120: | Zeile 228: | ||
return r | return r | ||
end -- facet() | end -- facet() | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
Zeile 173: | Zeile 236: | ||
-- adapt -- string, message ID after "templatedata-" | -- adapt -- string, message ID after "templatedata-" | ||
-- Returns string, with localized text | -- Returns string, with localized text | ||
− | + | local o = mw.message.new( "templatedata-" .. adapt ) | |
+ | if Foreign() then | ||
+ | o:inLanguage( Data.slang ) | ||
+ | end | ||
+ | return o:plain() | ||
end -- factory() | end -- factory() | ||
Zeile 217: | Zeile 284: | ||
return r | return r | ||
end -- failures() | end -- failures() | ||
+ | |||
+ | |||
+ | |||
+ | local function fair( adjust ) | ||
+ | -- Reduce text to one line of plain text, or noexport wikitext blocks | ||
+ | -- adjust -- string | ||
+ | -- Returns string, with adjusted text | ||
+ | local f = function ( a ) | ||
+ | return a:gsub( "%s*\n%s*", " " ) | ||
+ | :gsub( "%s%s+", " " ) | ||
+ | end | ||
+ | local r | ||
+ | if adjust:find( "<noexport>", 1, true ) then | ||
+ | local i = 1 | ||
+ | local j, k = adjust:find( "<noexport>", i, true ) | ||
+ | r = "" | ||
+ | while j do | ||
+ | if j > 1 then | ||
+ | r = r .. f( adjust:sub( i, j - 1 ) ) | ||
+ | end | ||
+ | i = k + 1 | ||
+ | j, k = adjust:find( "</noexport>", i, true ) | ||
+ | if j then | ||
+ | r = r .. adjust:sub( i, j - 1 ) | ||
+ | i = k + 1 | ||
+ | j, k = adjust:find( "<noexport>", i, true ) | ||
+ | else | ||
+ | Fault( "missing </noexport>" ) | ||
+ | end | ||
+ | end -- while j | ||
+ | r = r .. adjust:sub( i ) | ||
+ | else | ||
+ | r = f( adjust ) | ||
+ | end | ||
+ | return r | ||
+ | end -- fair() | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | local function fancy( advance, alert ) | ||
+ | -- Present JSON source | ||
+ | -- Parameter: | ||
+ | -- advance -- true, for nice | ||
+ | -- alert -- true, for visible | ||
+ | -- Returns string | ||
+ | local r | ||
+ | if Data.source then | ||
+ | local support = Config.jsonDebug | ||
+ | local css | ||
+ | if advance then | ||
+ | css = { height = "6em", | ||
+ | resize = "vertical" } | ||
+ | r = { [ 1 ] = "syntaxhighlight", | ||
+ | [ 2 ] = Data.source, | ||
+ | lang = "json", | ||
+ | style = table.concat( css, ";" ) } | ||
+ | if alert then | ||
+ | r.class( support ) | ||
+ | end | ||
+ | r = TemplateData.frame:callParserFunction( "#tag", r ) | ||
+ | else | ||
+ | css = { [ "font-size" ] = "77%", | ||
+ | [ "line-height" ] = "1.35" } | ||
+ | if alert then | ||
+ | css.resize = "vertical" | ||
+ | else | ||
+ | css.display = "none" | ||
+ | end | ||
+ | r = mw.html.create( "pre" ) | ||
+ | :addClass( support ) | ||
+ | :css( css ) | ||
+ | :wikitext( mw.text.encode( Data.source ) ) | ||
+ | r = tostring( r ) | ||
+ | end | ||
+ | r = "\n".. r | ||
+ | else | ||
+ | r = "" | ||
+ | end | ||
+ | return r | ||
+ | end -- fancy() | ||
local function faraway( alternatives ) | local function faraway( alternatives ) | ||
− | -- Retrieve | + | -- Retrieve best language version from multilingual text |
-- Parameter: | -- Parameter: | ||
-- alternatives -- table, to be evaluated | -- alternatives -- table, to be evaluated | ||
Zeile 228: | Zeile 376: | ||
-- 2 -- table of other versions, if any | -- 2 -- table of other versions, if any | ||
local n = 0 | local n = 0 | ||
+ | local variants = { } | ||
local r1, r2 | local r1, r2 | ||
− | |||
− | |||
− | |||
for k, v in pairs( alternatives ) do | for k, v in pairs( alternatives ) do | ||
if type( v ) == "string" then | if type( v ) == "string" then | ||
v = mw.text.trim( v ) | v = mw.text.trim( v ) | ||
− | if v | + | if v ~= "" then |
− | + | k = k:lower() | |
− | + | variants[ k ] = v | |
− | n = n + 1 | + | n = n + 1 |
end | end | ||
− | |||
− | |||
end | end | ||
end -- for k, v | end -- for k, v | ||
if n > 0 then | if n > 0 then | ||
− | for k, v in pairs( | + | local Multilingual = Fetch( "Multilingual", true ) |
− | + | if Multilingual and | |
+ | type( Multilingual.i18n ) == "function" then | ||
+ | local show, slang = Multilingual.i18n( variants ) | ||
+ | if show then | ||
+ | r1 = show | ||
+ | variants[ slang ] = nil | ||
+ | r2 = variants | ||
+ | end | ||
+ | end | ||
+ | if not r1 then | ||
+ | Foreign() | ||
+ | for k, v in pairs( variants ) do | ||
if n == 1 then | if n == 1 then | ||
r1 = v | r1 = v | ||
− | elseif | + | elseif Data.slang == k then |
− | + | variants[ k ] = nil | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
r1 = v | r1 = v | ||
− | r2 = | + | r2 = variants |
− | |||
end | end | ||
end -- for k, v | end -- for k, v | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
end | end | ||
− | if r2 then | + | if r2 and Multilingual then |
− | |||
for k, v in pairs( r2 ) do | for k, v in pairs( r2 ) do | ||
− | if v and not Multilingual.isLang( k ) then | + | if v and not Multilingual.isLang( k, true ) then |
− | Fault( string.format( " | + | Fault( string.format( "%s <code>lang=%s</code>", |
+ | "Invalid", | ||
k ) ) | k ) ) | ||
end | end | ||
Zeile 303: | Zeile 423: | ||
return r1, r2 | return r1, r2 | ||
end -- faraway() | end -- faraway() | ||
+ | |||
+ | |||
+ | |||
+ | local function fashioned( about, asked, assign ) | ||
+ | -- Create description head | ||
+ | -- Parameter: | ||
+ | -- about -- table, supposed to contain description | ||
+ | -- asked -- true, if mandatory description | ||
+ | -- assign -- <block>, if to be equipped | ||
+ | -- Returns <block>, with head, or nil | ||
+ | local para = assign or mw.html.create( "div" ) | ||
+ | local plus, r | ||
+ | if about and about.description then | ||
+ | if type( about.description ) == "string" then | ||
+ | para:wikitext( about.description ) | ||
+ | else | ||
+ | para:wikitext( about.description[ 1 ] ) | ||
+ | plus = mw.html.create( "ul" ) | ||
+ | plus:css( "text-align", "left" ) | ||
+ | for k, v in pairs( about.description[ 2 ] ) do | ||
+ | plus:node( mw.html.create( "li" ) | ||
+ | :node( mw.html.create( "code" ) | ||
+ | :wikitext( k ) ) | ||
+ | :node( mw.html.create( "br" ) ) | ||
+ | :wikitext( fair( v ) ) ) | ||
+ | end -- for k, v | ||
+ | if Config.loudly then | ||
+ | plus = mw.html.create( "div" ) | ||
+ | :css( "background-color", | ||
+ | "#" .. Config.debugmultilang ) | ||
+ | :node( plus ) | ||
+ | else | ||
+ | plus:addClass( "templatedata-maintain" ) | ||
+ | :css( "display", "none" ) | ||
+ | end | ||
+ | end | ||
+ | elseif Config.solo and asked then | ||
+ | para:addClass( "error" ) | ||
+ | :wikitext( Config.solo ) | ||
+ | Data.less = true | ||
+ | else | ||
+ | para = false | ||
+ | end | ||
+ | if para then | ||
+ | if plus then | ||
+ | r = mw.html.create( "div" ) | ||
+ | :node( para ) | ||
+ | :node( plus ) | ||
+ | else | ||
+ | r = para | ||
+ | end | ||
+ | end | ||
+ | return r | ||
+ | end -- fashioned() | ||
+ | |||
+ | |||
+ | |||
+ | local function fatten( access ) | ||
+ | -- Create table row for sub-headline | ||
+ | -- Parameter: | ||
+ | -- access -- string, with name | ||
+ | -- Returns <tr> | ||
+ | local param = Data.tree.params[ access ] | ||
+ | local sub, sort = access:match( "(=+)%s*(%S.*)$" ) | ||
+ | local headline = mw.html.create( string.format( "h%d", #sub ) ) | ||
+ | local r = mw.html.create( "tr" ) | ||
+ | local td = mw.html.create( "td" ) | ||
+ | :attr( "colspan", "5" ) | ||
+ | :attr( "data-sort-value", "!" .. sort ) | ||
+ | local s | ||
+ | if param.style then | ||
+ | s = type( param.style ) | ||
+ | if s == "table" then | ||
+ | td:css( param.style ) | ||
+ | elseif s == "string" then | ||
+ | td:cssText( param.style ) | ||
+ | end | ||
+ | end | ||
+ | s = fashioned( param, false, headline ) | ||
+ | if s then | ||
+ | headline = s | ||
+ | else | ||
+ | headline:wikitext( sort ) | ||
+ | end | ||
+ | td:node( headline ) | ||
+ | r:node( td ) | ||
+ | return r | ||
+ | end -- fatten() | ||
Zeile 316: | Zeile 524: | ||
end -- for k, v | end -- for k, v | ||
for i = 1, n do | for i = 1, n do | ||
− | for k, v in pairs( Data.heirs ) do | + | if Data.heirs then |
− | + | for k, v in pairs( Data.heirs ) do | |
− | + | if v and not Data.heirs[ v ] then | |
− | + | n = n - 1 | |
− | + | t[ k ].inherits = nil | |
− | + | Data.heirs[ k ] = nil | |
− | + | p2 = { } | |
− | + | t2 = { } | |
− | + | if p[ v ] then | |
− | + | for k2, v2 in pairs( p[ v ] ) do | |
− | + | p2[ k2 ] = v2 | |
− | + | end -- for k2, v2 | |
− | p2[ k2 ] = v2 | + | if p[ k ] then |
+ | for k2, v2 in pairs( p[ k ] ) do | ||
+ | if type( v2 ) ~= "nil" then | ||
+ | p2[ k2 ] = v2 | ||
+ | end | ||
+ | end -- for k2, v2 | ||
+ | end | ||
+ | p[ k ] = p2 | ||
+ | for k2, v2 in pairs( t[ v ] ) do | ||
+ | t2[ k2 ] = v2 | ||
+ | end -- for k2, v2 | ||
+ | for k2, v2 in pairs( t[ k ] ) do | ||
+ | if type( v2 ) ~= "nil" then | ||
+ | t2[ k2 ] = v2 | ||
+ | end | ||
+ | end -- for k2, v2 | ||
+ | t[ k ] = t2 | ||
+ | else | ||
+ | Fault( "No params[] inherits " .. v ) | ||
end | end | ||
− | end | + | end |
− | + | end -- for k, v | |
− | + | end | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | end | ||
end -- i = 1, n | end -- i = 1, n | ||
if n > 0 then | if n > 0 then | ||
Zeile 358: | Zeile 574: | ||
end | end | ||
end -- fathers() | end -- fathers() | ||
+ | |||
+ | |||
+ | |||
+ | local function favorize() | ||
+ | -- Local customization issues | ||
+ | local boole = { ["font-size"] = "125%" } | ||
+ | local l, cx = pcall( mw.loadData, | ||
+ | TemplateData.frame:getTitle() .. "/config" ) | ||
+ | local scripting | ||
+ | TemplateData.ltr = not mw.language.getContentLanguage():isRTL() | ||
+ | if TemplateData.ltr then | ||
+ | scripting = "left" | ||
+ | else | ||
+ | scripting = "right" | ||
+ | end | ||
+ | boole[ "margin-" .. scripting ] = "3em" | ||
+ | Permit.boole = { [false] = { css = boole, | ||
+ | lead = true, | ||
+ | show = "☐" }, | ||
+ | [true] = { css = boole, | ||
+ | lead = true, | ||
+ | show = "☑" } } | ||
+ | Permit.css = { } | ||
+ | for k, v in pairs( Permit.colors ) do | ||
+ | if k == "tableheadbg" then | ||
+ | k = "tablehead" | ||
+ | end | ||
+ | Permit.css[ k ] = { ["background-color"] = "#" .. v } | ||
+ | end -- for k, v | ||
+ | if type( cx ) == "table" then | ||
+ | local c, s | ||
+ | if type( cx.permit ) == "table" then | ||
+ | if type( cx.permit.boole ) == "table" then | ||
+ | if type( cx.permit.boole[ true ] ) == "table" then | ||
+ | Permit.boole[ false ] = cx.permit.boole[ false ] | ||
+ | end | ||
+ | if type( cx.permit.boole[ true ] ) == "table" then | ||
+ | Permit.boole[ true ] = cx.permit.boole[ true ] | ||
+ | end | ||
+ | end | ||
+ | if type( cx.permit.css ) == "table" then | ||
+ | for k, v in pairs( cx.permit.css ) do | ||
+ | if type( v ) == "table" then | ||
+ | Permit.css[ k ] = v | ||
+ | end | ||
+ | end -- for k, v | ||
+ | end | ||
+ | end | ||
+ | for k, v in pairs( Config.basicCnf ) do | ||
+ | s = type( cx[ k ] ) | ||
+ | if s == "string" or s == "table" then | ||
+ | Config[ v ] = cx[ k ] | ||
+ | end | ||
+ | end -- for k, v | ||
+ | end | ||
+ | if type( Config.subpage ) ~= "string" or | ||
+ | type( Config.suffix ) ~= "string" then | ||
+ | local got = mw.message.new( "templatedata-doc-subpage" ) | ||
+ | local suffix | ||
+ | if got:isDisabled() then | ||
+ | suffix = "doc" | ||
+ | else | ||
+ | suffix = got:plain() | ||
+ | end | ||
+ | if type( Config.subpage ) ~= "string" then | ||
+ | Config.subpage = string.format( "/%s$", suffix ) | ||
+ | end | ||
+ | if type( Config.suffix ) ~= "string" then | ||
+ | Config.suffix = string.format( "%%s/%s", suffix ) | ||
+ | end | ||
+ | end | ||
+ | end -- favorize() | ||
Zeile 379: | Zeile 667: | ||
local pointers = { } | local pointers = { } | ||
local points = { } | local points = { } | ||
+ | local given = { } | ||
for k, v in pairs( Data.tree.params ) do | for k, v in pairs( Data.tree.params ) do | ||
i = facet( k, 1 ) | i = facet( k, 1 ) | ||
+ | if type( v ) == "table" then | ||
+ | if type( v.label ) == "string" then | ||
+ | s = mw.text.trim( v.label ) | ||
+ | if s == "" then | ||
+ | s = k | ||
+ | end | ||
+ | else | ||
+ | s = k | ||
+ | end | ||
+ | if given[ s ] then | ||
+ | if given[ s ] == 1 then | ||
+ | local scream = "Parameter label '%s' detected multiple times" | ||
+ | Fault( string.format( scream, s ) ) | ||
+ | given[ s ] = 2 | ||
+ | end | ||
+ | else | ||
+ | given[ s ] = 1 | ||
+ | end | ||
+ | end | ||
if i then | if i then | ||
table.insert( points, i ) | table.insert( points, i ) | ||
Zeile 444: | Zeile 752: | ||
-- name and aliases | -- name and aliases | ||
− | code:wikitext( access ) | + | code:css( "font-size", "92%" ) |
+ | :css( "white-space", "nowrap" ) | ||
+ | :wikitext( access ) | ||
if not fine( access ) then | if not fine( access ) then | ||
code:addClass( "error" ) | code:addClass( "error" ) | ||
Zeile 485: | Zeile 795: | ||
-- description etc. | -- description etc. | ||
− | s = | + | s = fashioned( param ) |
if s then | if s then | ||
desc:node( s ) | desc:node( s ) | ||
+ | end | ||
+ | if param.style then | ||
+ | s = type( param.style ) | ||
+ | if s == "table" then | ||
+ | desc:css( param.style ) | ||
+ | elseif s == "string" then | ||
+ | desc:cssText( param.style ) | ||
+ | end | ||
end | end | ||
if param.default or param.example or param.autovalue then | if param.default or param.example or param.autovalue then | ||
local details = { "default", "example", "autovalue" } | local details = { "default", "example", "autovalue" } | ||
local dl = mw.html.create( "dl" ) | local dl = mw.html.create( "dl" ) | ||
− | local section, show | + | local dd, section, show |
for i = 1, #details do | for i = 1, #details do | ||
s = details[ i ] | s = details[ i ] | ||
show = param[ s ] | show = param[ s ] | ||
if show then | if show then | ||
+ | dd = mw.html.create( "dd" ) | ||
section = factory( "doc-param-" .. s ) | section = factory( "doc-param-" .. s ) | ||
+ | if param.type == "boolean" and | ||
+ | ( show == "0" or show == "1" ) then | ||
+ | local boole = Permit.boole[ ( show == "1" ) ] | ||
+ | if boole.lead == true then | ||
+ | dd:node( mw.html.create( "code" ) | ||
+ | :wikitext( show ) ) | ||
+ | :wikitext( " " ) | ||
+ | end | ||
+ | if type( boole.show ) == "string" then | ||
+ | local v = mw.html.create( "span" ) | ||
+ | :wikitext( boole.show ) | ||
+ | if boole.css then | ||
+ | v:css( boole.css ) | ||
+ | end | ||
+ | dd:node( v ) | ||
+ | end | ||
+ | if type( boole.suffix ) == "string" then | ||
+ | dd:wikitext( boole.suffix ) | ||
+ | end | ||
+ | if boole.lead == false then | ||
+ | dd:wikitext( " " ) | ||
+ | :node( mw.html.create( "code" ) | ||
+ | :wikitext( show ) ) | ||
+ | end | ||
+ | else | ||
+ | dd:wikitext( show ) | ||
+ | end | ||
dl:node( mw.html.create( "dt" ) | dl:node( mw.html.create( "dt" ) | ||
:wikitext( section ) ) | :wikitext( section ) ) | ||
− | :node( | + | :node( dd ) |
− | |||
end | end | ||
end -- i = 1, #details | end -- i = 1, #details | ||
Zeile 508: | Zeile 853: | ||
-- type | -- type | ||
+ | if type( param.type ) == "string" then | ||
+ | param.type = mw.text.trim( param.type ) | ||
+ | if param.type == "" then | ||
+ | param.type = false | ||
+ | end | ||
+ | end | ||
if param.type then | if param.type then | ||
s = Permit.types[ param.type ] | s = Permit.types[ param.type ] | ||
Zeile 515: | Zeile 866: | ||
Data.params[ access ].type = s | Data.params[ access ].type = s | ||
typed:wikitext( factory( "doc-param-type-" .. s ) ) | typed:wikitext( factory( "doc-param-type-" .. s ) ) | ||
− | : | + | :tag( "br" ) |
− | + | typed:node( mw.html.create( "span" ) | |
:addClass( "error" ) | :addClass( "error" ) | ||
:wikitext( param.type ) ) | :wikitext( param.type ) ) | ||
Data.lasting = true | Data.lasting = true | ||
else | else | ||
+ | local support = Config[ "support4" .. param.type ] | ||
s = factory( "doc-param-type-" .. param.type ) | s = factory( "doc-param-type-" .. param.type ) | ||
+ | if support then | ||
+ | s = string.format( "[[%s|%s]]", support, s ) | ||
+ | end | ||
typed:wikitext( s ) | typed:wikitext( s ) | ||
end | end | ||
Zeile 540: | Zeile 895: | ||
if param.required then | if param.required then | ||
mode = 1 | mode = 1 | ||
+ | if param.autovalue then | ||
+ | Fault( string.format( "autovalued <code>%s</code> required", | ||
+ | access ) ) | ||
+ | legal = false | ||
+ | end | ||
+ | if param.default then | ||
+ | Fault( string.format( "Defaulted <code>%s</code> required", | ||
+ | access ) ) | ||
+ | legal = false | ||
+ | end | ||
if param.deprecated then | if param.deprecated then | ||
Fault( string.format( "Required deprecated <code>%s</code>", | Fault( string.format( "Required deprecated <code>%s</code>", | ||
Zeile 560: | Zeile 925: | ||
if type( param.deprecated ) == "string" then | if type( param.deprecated ) == "string" then | ||
ranking:tag( "br" ) | ranking:tag( "br" ) | ||
− | + | ranking:wikitext( param.deprecated ) | |
end | end | ||
end | end | ||
Zeile 566: | Zeile 931: | ||
-- <tr> | -- <tr> | ||
r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) ) | r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) ) | ||
− | :css( | + | :css( Permit.css[ status ] ) |
:node( begin ) | :node( begin ) | ||
:node( code ) | :node( code ) | ||
Zeile 588: | Zeile 953: | ||
local r | local r | ||
if Data.tree and Data.tree.params then | if Data.tree and Data.tree.params then | ||
− | local tr = mw.html.create( "tr" ) | + | local tbl = mw.html.create( "table" ) |
+ | :addClass( "wikitable" ) | ||
+ | local tr = mw.html.create( "tr" ) | ||
feat() | feat() | ||
+ | if Data.order and #Data.order > 1 then | ||
+ | tbl:addClass( "sortable" ) | ||
+ | end | ||
+ | -- if Config.classTable then | ||
+ | -- tbl:addClass( Config.classTable ) | ||
+ | -- end | ||
+ | if Config.cssTable then | ||
+ | if type( Config.cssTable ) == "table" then | ||
+ | tbl:css( Config.cssTable ) | ||
+ | elseif type( Config.cssTable ) == "string" then | ||
+ | -- deprecated | ||
+ | tbl:cssText( Config.cssTable ) | ||
+ | end | ||
+ | end | ||
tr:node( mw.html.create( "th" ) | tr:node( mw.html.create( "th" ) | ||
:attr( "colspan", "2" ) | :attr( "colspan", "2" ) | ||
+ | :css( Permit.css.tablehead ) | ||
:wikitext( factory( "doc-param-name" ) ) ) | :wikitext( factory( "doc-param-name" ) ) ) | ||
:node( mw.html.create( "th" ) | :node( mw.html.create( "th" ) | ||
+ | :css( Permit.css.tablehead ) | ||
:wikitext( factory( "doc-param-desc" ) ) ) | :wikitext( factory( "doc-param-desc" ) ) ) | ||
:node( mw.html.create( "th" ) | :node( mw.html.create( "th" ) | ||
+ | :css( Permit.css.tablehead ) | ||
:wikitext( factory( "doc-param-type" ) ) ) | :wikitext( factory( "doc-param-type" ) ) ) | ||
:node( mw.html.create( "th" ) | :node( mw.html.create( "th" ) | ||
+ | :css( Permit.css.tablehead ) | ||
:wikitext( factory( "doc-param-status" ) ) ) | :wikitext( factory( "doc-param-status" ) ) ) | ||
− | + | tbl:newline() | |
− | + | -- :node( mw.html.create( "thead" ) | |
− | + | :node( tr ) | |
− | + | -- ) | |
− | + | :newline() | |
− | |||
− | -- | ||
− | |||
− | -- | ||
− | |||
if Data.order then | if Data.order then | ||
+ | local leave, s | ||
for i = 1, #Data.order do | for i = 1, #Data.order do | ||
− | + | s = Data.order[ i ] | |
+ | if s:sub( 1, 1 ) == "=" then | ||
+ | leave = true | ||
+ | tbl:node( fatten( s ) ) | ||
+ | Data.order[ i ] = false | ||
+ | elseif s:match( "[=|]" ) then | ||
+ | Fault( string.format( "Bad param <code>%s</code>", | ||
+ | s ) ) | ||
+ | else | ||
+ | tbl:node( feature( s ) ) | ||
+ | end | ||
end -- for i = 1, #Data.order | end -- for i = 1, #Data.order | ||
+ | if leave then | ||
+ | for i = #Data.order, 1, -1 do | ||
+ | if not Data.order[ i ] then | ||
+ | table.remove( Data.order, i ) | ||
+ | end | ||
+ | end -- for i = #Data.order, 1, -1 | ||
+ | end | ||
+ | Data.tag.paramOrder = Data.order | ||
end | end | ||
− | + | if Config.cssTabWrap or Data.scroll then | |
− | + | r = mw.html.create( "div" ) | |
− | + | if type( Config.cssTabWrap ) == "table" then | |
− | + | r:css( Config.cssTabWrap ) | |
− | + | elseif type( Config.cssTabWrap ) == "string" then | |
− | + | -- deprecated | |
− | + | r:cssText( Config.cssTabWrap ) | |
− | + | end | |
− | + | if Data.scroll then | |
− | + | r:css( "height", Data.scroll ) | |
− | + | :css( "overflow", "auto" ) | |
− | + | end | |
− | + | r:node( tbl ) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | r | ||
− | |||
else | else | ||
− | + | r = tbl | |
end | end | ||
end | end | ||
return r | return r | ||
− | end -- | + | end -- features() |
− | local function finalize() | + | local function finalize( advance ) |
-- Wrap presentation into frame | -- Wrap presentation into frame | ||
+ | -- Parameter: | ||
+ | -- advance -- true, for nice | ||
-- Returns string | -- Returns string | ||
− | local r | + | local r, lapsus |
if Data.div then | if Data.div then | ||
r = tostring( Data.div ) | r = tostring( Data.div ) | ||
Zeile 658: | Zeile 1.046: | ||
r = Data.strip | r = Data.strip | ||
else | else | ||
− | r = "" | + | lapsus = true |
+ | r = "" | ||
+ | end | ||
+ | r = r .. failures() | ||
+ | if Data.source then | ||
+ | local live = ( advance or lapsus ) | ||
+ | if not live then | ||
+ | live = TemplateData.frame:preprocess( "{{REVISIONID}}" ) | ||
+ | live = ( live == "" ) | ||
+ | end | ||
+ | if live then | ||
+ | r = r .. fancy( advance, lapsus ) | ||
+ | end | ||
end | end | ||
− | return r | + | return r |
end -- finalize() | end -- finalize() | ||
Zeile 689: | Zeile 1.089: | ||
local r | local r | ||
if adjust then | if adjust then | ||
− | r = adjust | + | r = adjust:gsub( "\n", " " ) |
if r:find( "<noexport>", 1, true ) then | if r:find( "<noexport>", 1, true ) then | ||
r = r:gsub( "<noexport>(.*)</noexport>", "" ) | r = r:gsub( "<noexport>(.*)</noexport>", "" ) | ||
end | end | ||
− | if r:find( "''", 1, true ) | + | if r:find( "''", 1, true ) then |
− | local Text = | + | r = r:gsub( "'''", "" ):gsub( "''", "" ) |
− | r = Text.getPlain( r ) | + | end |
+ | if r:find( "<", 1, true ) then | ||
+ | local Text = Fetch( "Text" ) | ||
+ | r = Text.getPlain( r:gsub( "<br */?>", "\r\n" ) ) | ||
end | end | ||
if r:find( "[", 1, true ) then | if r:find( "[", 1, true ) then | ||
− | local WLink = | + | local WLink = Fetch( "WLink" ) |
if WLink.isBracketedURL( r ) then | if WLink.isBracketedURL( r ) then | ||
r = r:gsub( "%[([hf]tt?ps?://%S+) [^%]]+%]", "%1" ) | r = r:gsub( "%[([hf]tt?ps?://%S+) [^%]]+%]", "%1" ) | ||
Zeile 780: | Zeile 1.183: | ||
if scope then | if scope then | ||
s = type( v ) | s = type( v ) | ||
− | if s == "string" then | + | if s == "string" and k ~= "format" then |
v = mw.text.trim( v ) | v = mw.text.trim( v ) | ||
end | end | ||
Zeile 786: | Zeile 1.189: | ||
if scope:find( "I18N", 1, true ) then | if scope:find( "I18N", 1, true ) then | ||
if s == "string" then | if s == "string" then | ||
− | elem = | + | elem = fair( v ) |
− | + | elseif s == "table" then | |
− | |||
local translated | local translated | ||
v, translated = faraway( v ) | v, translated = faraway( v ) | ||
− | if translated and | + | if v then |
− | + | if translated and | |
− | + | k == "description" then | |
− | + | elem = { [ 1 ] = fair( v ), | |
+ | [ 2 ] = translated } | ||
+ | else | ||
+ | elem = fair( v ) | ||
+ | end | ||
+ | else | ||
+ | elem = false | ||
+ | end | ||
+ | end | ||
+ | if type( v ) == "string" then | ||
+ | if k == "deprecated" then | ||
+ | if v == "1" then | ||
+ | v = true | ||
+ | elseif v == "0" then | ||
+ | v = false | ||
+ | end | ||
+ | elem = v | ||
+ | elseif scope:find( "nowiki", 1, true ) then | ||
+ | elem = mw.text.nowiki( v ) | ||
+ | elem = elem:gsub( " \n", "<br>" ) | ||
+ | v = v:gsub( string.char( 13 ), "" ) | ||
+ | else | ||
+ | v = flat( v ) | ||
+ | end | ||
+ | elseif s == "boolean" then | ||
+ | if scope:find( "boolean", 1, true ) then | ||
+ | elem = v | ||
else | else | ||
− | + | s = "Type <code>boolean</code> bad for " | |
+ | .. f( k, slot ) | ||
+ | Fault( s ) | ||
end | end | ||
− | |||
end | end | ||
else | else | ||
− | |||
if k == "params" and not access then | if k == "params" and not access then | ||
v = nil | v = nil | ||
elem = nil | elem = nil | ||
+ | elseif k == "format" and not access then | ||
+ | elem = mw.text.decode( v ) | ||
+ | v = nil | ||
elseif k == "inherits" then | elseif k == "inherits" then | ||
+ | elem = v | ||
if not Data.heirs then | if not Data.heirs then | ||
Data.heirs = { } | Data.heirs = { } | ||
end | end | ||
Data.heirs[ slot ] = v | Data.heirs[ slot ] = v | ||
− | v = nil | + | v = nil |
+ | elseif k == "style" then | ||
+ | elem = v | ||
+ | v = nil | ||
+ | elseif s == "string" then | ||
+ | v = mw.text.nowiki( v ) | ||
+ | elem = v | ||
+ | else | ||
+ | elem = v | ||
end | end | ||
end | end | ||
Zeile 832: | Zeile 1.272: | ||
if not tag then | if not tag then | ||
if access then | if access then | ||
− | if not Data.params then | + | if type( v ) == "string" and |
− | Data.params = { } | + | v.sub( 1, 1 ) == "=" then |
+ | v = nil | ||
+ | else | ||
+ | if not Data.params then | ||
+ | Data.params = { } | ||
+ | end | ||
+ | Data.params[ slot ] = { } | ||
+ | tag = Data.params[ slot ] | ||
end | end | ||
− | |||
− | |||
else | else | ||
Data.tag = { } | Data.tag = { } | ||
Zeile 842: | Zeile 1.287: | ||
end | end | ||
end | end | ||
− | tag[ k ] = v | + | if type( v ) ~= "nil" then |
+ | tag[ k ] = v | ||
+ | end | ||
end | end | ||
else | else | ||
Zeile 861: | Zeile 1.308: | ||
local function format() | local function format() | ||
+ | -- Build formatted element | ||
+ | -- Returns <inline> | ||
+ | local source = Data.tree.format:lower() | ||
+ | local r, s | ||
+ | if source == "inline" or source == "block" then | ||
+ | r = mw.html.create( "i" ) | ||
+ | :wikitext( source ) | ||
+ | else | ||
+ | local code | ||
+ | if source:find( "|", 1, true ) then | ||
+ | local scan = "^[\n ]*%{%{[\n _]*|[\n _]*=[\n _]*%}%}[\n ]*$" | ||
+ | if source:match( scan ) then | ||
+ | code = source:gsub( "\n", "N" ) | ||
+ | else | ||
+ | s = mw.text.nowiki( source ):gsub( "\n", "\n" ) | ||
+ | s = tostring( mw.html.create( "code" ) | ||
+ | :wikitext( s ) ) | ||
+ | Fault( "Invalid format " .. s ) | ||
+ | source = false | ||
+ | end | ||
+ | else | ||
+ | local words = mw.text.split( source, "%s+" ) | ||
+ | local show, start, support, unknown | ||
+ | for i = 1, #words do | ||
+ | s = words[ i ] | ||
+ | if i == 1 then | ||
+ | start = s | ||
+ | end | ||
+ | support = Permit.builder[ s ] | ||
+ | if support == start or | ||
+ | support == "*" then | ||
+ | Permit.builder[ s ] = true | ||
+ | elseif s:match( "^[1-9]%d?" ) and | ||
+ | Permit.builder.align then | ||
+ | Permit.builder.align = tonumber( s ) | ||
+ | else | ||
+ | if unknown then | ||
+ | unknown = string.format( "%s %s", unknown, s ) | ||
+ | else | ||
+ | unknown = s | ||
+ | end | ||
+ | end | ||
+ | end -- i = 1, #words | ||
+ | if unknown then | ||
+ | s = tostring( mw.html.create( "code" ) | ||
+ | :css( "white-space", "nowrap" ) | ||
+ | :wikitext( s ) ) | ||
+ | Fault( "Unknown/misplaced format keyword " .. s ) | ||
+ | source = false | ||
+ | start = false | ||
+ | end | ||
+ | if start == "inline" then | ||
+ | if Permit.builder.half == true then | ||
+ | show = "inline half" | ||
+ | code = "{{_ |_=_}}" | ||
+ | elseif Permit.builder.grouped == true then | ||
+ | show = "inline grouped" | ||
+ | code = "{{_ | _=_}}" | ||
+ | elseif Permit.builder.spaced == true then | ||
+ | show = "inline spaced" | ||
+ | code = "{{_ | _ = _ }}" | ||
+ | end | ||
+ | if Permit.builder.newlines == true then | ||
+ | show = show or "inline" | ||
+ | code = code or "{{_|_=_}}" | ||
+ | show = show .. " newlines" | ||
+ | code = string.format( "N%sN", code ) | ||
+ | end | ||
+ | elseif start == "block" then | ||
+ | local space = "" -- amid "|" and name | ||
+ | local spaced = " " -- preceding "=" | ||
+ | local spacer = " " -- following "=" | ||
+ | local suffix = "N" -- closing "}}" on new line | ||
+ | show = "block" | ||
+ | if Permit.builder.indent == true then | ||
+ | start = " " | ||
+ | show = "block indent" | ||
+ | else | ||
+ | start = "" | ||
+ | end | ||
+ | if Permit.builder.compressed == true then | ||
+ | spaced = "" | ||
+ | spacer = "" | ||
+ | show = show .. " compressed" | ||
+ | if Permit.builder.last == true then | ||
+ | show = show .. " last" | ||
+ | else | ||
+ | suffix = "" | ||
+ | end | ||
+ | else | ||
+ | if Permit.builder.lead == true then | ||
+ | show = show .. " lead" | ||
+ | space = " " | ||
+ | end | ||
+ | if type( Permit.builder.align ) ~= "string" then | ||
+ | local n | ||
+ | s = " align" | ||
+ | if Permit.builder.align == true then | ||
+ | n = 0 | ||
+ | if type( Data.got ) == "table" and | ||
+ | type( Data.got.params ) == "table" then | ||
+ | for k, v in pairs( Data.got.params ) do | ||
+ | if type( v ) == "table" and | ||
+ | not v.deprecated and | ||
+ | type( k ) == "string" then | ||
+ | k = mw.ustring.len( k ) | ||
+ | if k > n then | ||
+ | n = k | ||
+ | end | ||
+ | end | ||
+ | end -- for k, v | ||
+ | end | ||
+ | else | ||
+ | n = Permit.builder.align | ||
+ | if type( n ) == "number" and n > 1 then | ||
+ | s = string.format( "%s %d", s, n ) | ||
+ | else | ||
+ | n = 0 -- How comes? | ||
+ | end | ||
+ | end | ||
+ | if n > 1 then | ||
+ | spaced = string.rep( "_", n ) .. " " | ||
+ | end | ||
+ | show = show .. s | ||
+ | elseif Permit.builder.after == true then | ||
+ | spaced = "" | ||
+ | show = show .. " after" | ||
+ | elseif Permit.builder.dense == true then | ||
+ | spaced = "" | ||
+ | spacer = "" | ||
+ | show = show .. " dense" | ||
+ | end | ||
+ | if Permit.builder.last == true then | ||
+ | suffix = spacer | ||
+ | show = show .. " last" | ||
+ | end | ||
+ | end | ||
+ | code = string.format( "N{{_N%s|%s_%s=%s_%s}}N", | ||
+ | start, | ||
+ | space, | ||
+ | spaced, | ||
+ | spacer, | ||
+ | suffix ) | ||
+ | if show == "block" then | ||
+ | show = "block newlines" | ||
+ | end | ||
+ | end | ||
+ | if show then | ||
+ | r = mw.html.create( "span" ) | ||
+ | :wikitext( show ) | ||
+ | end | ||
+ | end | ||
+ | if code then | ||
+ | source = code:gsub( "N", "\n" ) | ||
+ | code = mw.text.nowiki( code ):gsub( "N", "\n" ) | ||
+ | code = mw.html.create( "code" ) | ||
+ | :css( "margin-left", "1em" ) | ||
+ | :css( "margin-right", "1em" ) | ||
+ | :wikitext( code ) | ||
+ | if r then | ||
+ | r = mw.html.create( "span" ) | ||
+ | :node( r ) | ||
+ | :node( code ) | ||
+ | else | ||
+ | r = code | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | if source then | ||
+ | Data.tag.format = source | ||
+ | end | ||
+ | return r | ||
+ | end -- format() | ||
+ | |||
+ | |||
+ | |||
+ | local function formatter() | ||
-- Build presented documentation | -- Build presented documentation | ||
-- Returns <div> | -- Returns <div> | ||
local r = mw.html.create( "div" ) | local r = mw.html.create( "div" ) | ||
− | local | + | local x = fashioned( Data.tree, true, r ) |
− | if s then | + | local s |
− | r:node( | + | if x then |
+ | r = x | ||
+ | end | ||
+ | if Data.leading then | ||
+ | local toc = mw.html.create( "div" ) | ||
+ | local shift | ||
+ | if Config.suppressTOCnum then | ||
+ | toc:addClass( Config.suppressTOCnum ) | ||
+ | if type( Config.stylesTOCnum ) == "string" then | ||
+ | local src = Config.stylesTOCnum .. "/styles.css" | ||
+ | s = TemplateData.frame:extensionTag( "templatestyles", | ||
+ | nil, | ||
+ | { src = src } ) | ||
+ | r:newline() | ||
+ | :node( s ) | ||
+ | end | ||
+ | end | ||
+ | toc:css( "margin-top", "0.5em" ) | ||
+ | :wikitext( "__TOC__" ) | ||
+ | if Data.sibling then | ||
+ | local block = mw.html.create( "div" ) | ||
+ | if TemplateData.ltr then | ||
+ | shift = "right" | ||
+ | else | ||
+ | shift = "left" | ||
+ | end | ||
+ | block:css( "float", shift ) | ||
+ | :wikitext( Data.sibling ) | ||
+ | r:newline() | ||
+ | :node( block ) | ||
+ | :newline() | ||
+ | end | ||
+ | r:newline() | ||
+ | :node( toc ) | ||
+ | :newline() | ||
+ | if shift then | ||
+ | r:node( mw.html.create( "div" ) | ||
+ | :css( "clear", shift ) ) | ||
+ | :newline() | ||
+ | end | ||
end | end | ||
s = features() | s = features() | ||
if s then | if s then | ||
+ | if Data.leading then | ||
+ | r:node( mw.html.create( "h2" ) | ||
+ | :wikitext( factory( "doc-params" ) ) ) | ||
+ | :newline() | ||
+ | end | ||
r:node( s ) | r:node( s ) | ||
+ | end | ||
+ | if Data.shared then | ||
+ | local global = mw.html.create( "div" ) | ||
+ | :attr( "id", "templatedata-global" ) | ||
+ | local shift | ||
+ | if TemplateData.ltr then | ||
+ | shift = "right" | ||
+ | else | ||
+ | shift = "left" | ||
+ | end | ||
+ | global:css( "float", shift ) | ||
+ | :wikitext( string.format( "[[%s|%s]]", | ||
+ | Data.shared, "Global" ) ) | ||
+ | r:newline() | ||
+ | :node( global ) | ||
end | end | ||
if Data.tree and Data.tree.format then | if Data.tree and Data.tree.format then | ||
− | local e | + | local e = format() |
− | + | if e then | |
− | if | + | local show = "Format" |
− | + | if Config.supportFormat then | |
− | + | show = string.format( "[[%s|%s]]", | |
− | + | Config.supportFormat, show ) | |
+ | end | ||
+ | r:node( mw.html.create( "p" ) | ||
+ | :wikitext( show .. ": " ) | ||
+ | :node( e ) ) | ||
end | end | ||
− | |||
− | |||
− | |||
− | |||
end | end | ||
return r | return r | ||
− | end -- | + | end -- formatter() |
Zeile 913: | Zeile 1.596: | ||
end | end | ||
end | end | ||
− | Data.div:node( | + | Data.div:node( formatter() ) |
− | Data.slim = flush() | + | if not Data.lazy then |
− | + | Data.slim = flush() | |
− | + | if TemplateData.frame then | |
− | + | local div = mw.html.create( "div" ) | |
− | + | local tdata = { [ 1 ] = "templatedata", | |
− | + | [ 2 ] = Data.slim } | |
− | + | Data.strip = TemplateData.frame:callParserFunction( "#tag", | |
− | + | tdata ) | |
− | + | div:wikitext( Data.strip ) | |
− | div:css( "display", "none" ) | + | if Config.loudly then |
+ | Data.div:node( mw.html.create( "hr" ) | ||
+ | :css( { height = "7ex" } ) ) | ||
+ | else | ||
+ | div:css( "display", "none" ) | ||
+ | end | ||
+ | Data.div:node( div ) | ||
end | end | ||
− | Data. | + | end |
+ | if Data.lasting then | ||
+ | Fault( "deprecated type syntax" ) | ||
+ | end | ||
+ | if Data.less then | ||
+ | Fault( Config.solo ) | ||
end | end | ||
end -- full() | end -- full() | ||
Zeile 938: | Zeile 1.632: | ||
-- Returns string | -- Returns string | ||
local source | local source | ||
− | for k, v in pairs( Config ) do | + | favorize() |
+ | -- deprecated: | ||
+ | for k, v in pairs( Config.basicCnf ) do | ||
if adapt[ k ] and adapt[ k ] ~= "" then | if adapt[ k ] and adapt[ k ] ~= "" then | ||
Config[ v ] = adapt[ k ] | Config[ v ] = adapt[ k ] | ||
Zeile 944: | Zeile 1.640: | ||
end -- for k, v | end -- for k, v | ||
Config.loudly = faculty( arglist.debug or adapt.debug ) | Config.loudly = faculty( arglist.debug or adapt.debug ) | ||
− | if mw. | + | Data.lazy = faculty( arglist.lazy ) and not Config.loudly |
− | + | Data.leading = faculty( arglist.TOC ) | |
− | end | + | if Data.leading and arglist.TOCsibling then |
+ | Data.sibling = mw.text.trim( arglist.TOCsibling ) | ||
+ | end | ||
+ | if arglist.lang then | ||
+ | Data.slang = arglist.lang:lower() | ||
+ | elseif adapt.lang then | ||
+ | Data.slang = adapt.lang:lower() | ||
+ | end | ||
if arglist.JSON then | if arglist.JSON then | ||
source = arglist.JSON | source = arglist.JSON | ||
+ | elseif arglist.Global then | ||
+ | source = TemplateData.getGlobalJSON( arglist.Global, | ||
+ | arglist.Local ) | ||
elseif arglist[ 1 ] then | elseif arglist[ 1 ] then | ||
local s = mw.text.trim( arglist[ 1 ] ) | local s = mw.text.trim( arglist[ 1 ] ) | ||
Zeile 960: | Zeile 1.666: | ||
Data.strip = s | Data.strip = s | ||
end | end | ||
+ | end | ||
+ | if type( arglist.vertical ) == "string" and | ||
+ | arglist.vertical:match( "^%d*%.?%d+[emprx]+$" ) then | ||
+ | Data.scroll = arglist.vertical | ||
end | end | ||
if not source then | if not source then | ||
Zeile 965: | Zeile 1.675: | ||
source = find() | source = find() | ||
if not source and | if not source and | ||
− | |||
not Data.title.text:match( Config.subpage ) then | not Data.title.text:match( Config.subpage ) then | ||
local s = string.format( Config.suffix, | local s = string.format( Config.suffix, | ||
Zeile 975: | Zeile 1.684: | ||
end | end | ||
end | end | ||
− | TemplateData.getPlainJSON( source ) | + | if not Data.lazy then |
− | return finalize() | + | if not Data.title then |
+ | Data.title = mw.title.getCurrentTitle() | ||
+ | end | ||
+ | Data.lazy = Data.title.text:match( Config.subpage ) | ||
+ | end | ||
+ | if type( source ) == "string" then | ||
+ | TemplateData.getPlainJSON( source ) | ||
+ | end | ||
+ | return finalize( faculty( arglist.source ) ) | ||
end -- furnish() | end -- furnish() | ||
− | TemplateData.failsafe = function ( | + | TemplateData.failsafe = function ( atleast ) |
+ | -- Retrieve versioning and check for compliance | ||
+ | -- Precondition: | ||
+ | -- atleast -- string, with required version or "wikidata" | ||
+ | -- or false | ||
+ | -- Postcondition: | ||
+ | -- Returns string with appropriate version, or false | ||
+ | local since = atleast | ||
local r | local r | ||
− | if not | + | if since == "wikidata" then |
− | + | local item = TemplateData.item | |
− | + | since = false | |
− | + | if type( item ) == "number" and item > 0 then | |
+ | local entity = mw.wikibase.getEntity( string.format( "Q%d", | ||
+ | item ) ) | ||
+ | if type( entity ) == "table" then | ||
+ | local vsn = entity:formatPropertyValues( "P348" ) | ||
+ | if type( vsn ) == "table" and | ||
+ | type( vsn.value ) == "string" and | ||
+ | vsn.value ~= "" then | ||
+ | r = vsn.value | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | if not r then | ||
+ | if not since or since <= TemplateData.serial then | ||
+ | r = TemplateData.serial | ||
+ | else | ||
+ | r = false | ||
+ | end | ||
end | end | ||
return r | return r | ||
end -- TemplateData.failsafe() | end -- TemplateData.failsafe() | ||
+ | |||
+ | |||
+ | |||
+ | TemplateData.getGlobalJSON = function ( access, adapt ) | ||
+ | -- Retrieve TemplateData from a global repository (JSON) | ||
+ | -- Parameter: | ||
+ | -- access -- string, with page specifier (on WikiMedia Commons) | ||
+ | -- adapt -- JSON string or table with local overrides | ||
+ | -- Returns true, if succeeded | ||
+ | local plugin = Fetch( "/global" ) | ||
+ | local r | ||
+ | if type( plugin ) == "table" and | ||
+ | type( plugin.fetch ) == "function" then | ||
+ | local s, got = plugin.fetch( access, adapt ) | ||
+ | if got then | ||
+ | Data.got = got | ||
+ | Data.order = got.paramOrder | ||
+ | Data.shared = s | ||
+ | r = true | ||
+ | full() | ||
+ | else | ||
+ | Fault( s ) | ||
+ | end | ||
+ | end | ||
+ | return r | ||
+ | end -- TemplateData.getGlobalJSON() | ||
Zeile 999: | Zeile 1.767: | ||
-- Returns string, or not | -- Returns string, or not | ||
if type( adapt ) == "string" then | if type( adapt ) == "string" then | ||
+ | local JSONutil = Fetch( "JSONutil", true ) | ||
Data.source = adapt | Data.source = adapt | ||
free() | free() | ||
− | Data.got = mw.text.jsonDecode | + | if JSONutil then |
− | if Data.got then | + | local Multilingual = Fetch( "Multilingual", true ) |
+ | local f | ||
+ | if Multilingual then | ||
+ | f = Multilingual.i18n | ||
+ | end | ||
+ | Data.got = JSONutil.fetch( Data.source, true, f ) | ||
+ | else | ||
+ | local lucky | ||
+ | lucky, Data.got = pcall( mw.text.jsonDecode, Data.source ) | ||
+ | end | ||
+ | if type( Data.got ) == "table" then | ||
full() | full() | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
elseif not Data.strip then | elseif not Data.strip then | ||
− | Fault( "fatal JSON error" ) | + | Fault( "fatal JSON error: " .. Data.got ) |
end | end | ||
end | end |
Aktuelle Version vom 6. September 2019, 12:54 Uhr
local TemplateData = { suite = "TemplateData",
serial = "2019-07-02", item = 46997995 }
--[=[ improve template:TemplateData ]=]
local Config = {
-- multiple option names mapped into unique internal fields basicCnf = { catProblem = "strange", classNoNumTOC = "suppressTOCnum", cssParWrap = "cssTabWrap", cssParams = "cssTable", docpageCreate = "suffix", docpageDetect = "subpage", helpBoolean = "support4boolean", helpContent = "support4content", helpDate = "support4date", helpFile = "support4wiki-file-name", helpFormat = "supportFormat", helpLine = "support4line", helpNumber = "support4number", helpPage = "support4wiki-page-name", helpString = "support4string", helpTemplate = "support4wiki-template-name", helpURL = "support4url", helpUser = "support4wiki-user-name", msgDescMiss = "solo", tStylesTOCnum = "stylesTOCnum" },
-- classParams = "classTable", -- classTable = false, -- class for params table
debugmultilang = "C0C0C0", loudly = false, -- show exported element, etc. solo = false, -- complaint on missing description strange = false, -- title of maintenance category cssTable = false, -- styles for params table cssTabWrap = false, -- styles for params table wrapper debug = false, subpage = false, -- pattern to identify subpage suffix = false, -- subpage creation scheme suppressTOCnum = false, -- class for TOC number suppression jsonDebug = "json-code-lint" -- class for jsonDebug tool
} local Data = {
div = false, --
got = false, -- table, initial templatedata object heirs = false, -- table, params that are inherited less = false, -- main description missing lasting = false, -- old syntax encountered lazy = false, -- doc mode; do not generate effective <templatedata> leading = false, -- show TOC
-- low = false, -- 1= mode
order = false, -- parameter sequence params = false, -- table, exported parameters scream = false, -- error messages sibling = false, -- TOC juxtaposed slang = nil, -- project/user language code slim = false, -- JSON reduced to plain source = false, -- JSON input strip = false, -- <templatedata> evaluation tag = false, -- table, exported root element title = false, -- page tree = false -- table, rewritten templatedata object
} local Permit = {
builder = { after = "block", align = "block", block = "block", compressed = "block", dense = "block", grouped = "inline", half = "inline", indent = "block", inline = "inline", last = "block", lead = "block", newlines = "*", spaced = "inline" }, colors = { tableheadbg = "B3B7FF", required = "EAF3FF", suggested = "FFFFFF", optional = "EAECF0", deprecated = "FFCBCB" }, params = { aliases = "table", autovalue = "string", default = "string table I18N nowiki", deprecated = "boolean string I18N", description = "string table I18N", example = "string table I18N nowiki", label = "string table I18N", inherits = "string", required = "boolean", style = "string table", suggested = "boolean", type = "string" }, root = { description = "string table I18N", format = "string", maps = "table", params = "table", paramOrder = "table", sets = "table" }, search = "[{,]%%s*(['\"])%s%%1%%s*:%%s*%%{", types = { boolean = true, content = true, date = true, line = true, number = true, string = true, unknown = true, url = true, ["wiki-file-name"] = true, ["wiki-page-name"] = true, ["wiki-template-name"] = true, ["wiki-user-name"] = true, ["unbalanced-wikitext"] = true, ["string/line"] = "line", ["string/wiki-page-name"] = "wiki-page-name", ["string/wiki-user-name"] = "wiki-user-name" }
}
local function Fault( alert )
-- Memorize error message -- Parameter: -- alert -- string, error message if Data.scream then Data.scream = string.format( "%s *** %s", Data.scream, alert ) else Data.scream = alert end
end -- Fault()
local function Fetch( ask, allow )
-- Fetch module -- Parameter: -- ask -- string, with name -- "/global" -- "JSONutil" -- "Multilingual" -- "Text" -- "WLink" -- allow -- true: no error if unavailable -- Returns table of module -- error: Module not available local sign = ask local r, stem if sign:sub( 1, 1 ) == "/" then sign = TemplateData.frame:getTitle() .. sign else stem = sign sign = "Module:" .. stem end if TemplateData.extern then r = TemplateData.extern[ sign ] else TemplateData.extern = { } end if not r then local lucky, g = pcall( require, sign ) if type( g ) == "table" then if stem and type( g[ stem ] ) == "function" then r = g[ stem ]() else r = g end TemplateData.extern[ sign ] = r elseif not allow then error( string.format( "Fetch(%s) %s", sign, g ) ) end end return r
end -- Fetch()
local function Foreign()
-- Guess human language -- Returns slang, or not if type( Data.slang ) == "nil" then local Multilingual = Fetch( "Multilingual", true ) if Multilingual and type( Multilingual.userLangCode ) == "function" then Data.slang = Multilingual.userLangCode() else Data.slang = mw.language.getContentLanguage():getCode() :lower() end end if Data.slang and mw.ustring.codepoint( Data.slang, 1, 1 ) > 122 then Data.slang = false end return Data.slang
end -- Foreign()
local function facet( ask, at )
-- Find physical position of parameter definition in JSON -- Parameter: -- ask -- string, parameter name -- at -- number, physical position within definition -- Returns number, or nil local seek = string.format( Permit.search, ask:gsub( "%%", "%%%%" ) :gsub( "([%-.()+*?^$%[%]])", "%%%1" ) ) local i, k = Data.source:find( seek, at ) local r, slice, source while i and not r do source = Data.source:sub( k + 1 ) slice = source:match( "^%s*\"([^\"]+)\"s*:" ) if not slice then slice = source:match( "^%s*'([^']+)'%s*:" ) end if ( slice and Permit.params[ slice ] ) or source:match( "^%s*%}" ) then r = k else i, k = Data.source:find( seek, k ) end end -- while i return r
end -- facet()
local function factory( adapt )
-- Retrieve localized text from system message -- Parameter: -- adapt -- string, message ID after "templatedata-" -- Returns string, with localized text local o = mw.message.new( "templatedata-" .. adapt ) if Foreign() then o:inLanguage( Data.slang ) end return o:plain()
end -- factory()
local function faculty( adjust )
-- Test template arg for boolean -- adjust -- string or nil -- Returns boolean local s = type( adjust ) local r if s == "string" then r = mw.text.trim( adjust ) r = ( r ~= "" and r ~= "0" ) elseif s == "boolean" then r = adjust else r = false end return r
end -- faculty()
local function failures()
-- Retrieve error collection and category -- Returns string local r if Data.scream then local e = mw.html.create( "span" ) :addClass( "error" ) :wikitext( Data.scream ) r = tostring( e ) mw.addWarning( "TemplateData
" .. Data.scream ) if Config.strange then r = string.format( "%s", r, Config.strange ) end else r = "" end return r
end -- failures()
local function fair( adjust )
-- Reduce text to one line of plain text, or noexport wikitext blocks -- adjust -- string -- Returns string, with adjusted text local f = function ( a ) return a:gsub( "%s*\n%s*", " " ) :gsub( "%s%s+", " " ) end local r if adjust:find( "<noexport>", 1, true ) then local i = 1 local j, k = adjust:find( "<noexport>", i, true ) r = "" while j do if j > 1 then r = r .. f( adjust:sub( i, j - 1 ) ) end i = k + 1 j, k = adjust:find( "</noexport>", i, true ) if j then r = r .. adjust:sub( i, j - 1 ) i = k + 1 j, k = adjust:find( "<noexport>", i, true ) else Fault( "missing </noexport>" ) end end -- while j r = r .. adjust:sub( i ) else r = f( adjust ) end return r
end -- fair()
local function fancy( advance, alert )
-- Present JSON source -- Parameter: -- advance -- true, for nice -- alert -- true, for visible -- Returns string local r if Data.source then local support = Config.jsonDebug local css if advance then css = { height = "6em", resize = "vertical" } r = { [ 1 ] = "syntaxhighlight", [ 2 ] = Data.source, lang = "json", style = table.concat( css, ";" ) } if alert then r.class( support ) end r = TemplateData.frame:callParserFunction( "#tag", r ) else css = { [ "font-size" ] = "77%", [ "line-height" ] = "1.35" } if alert then css.resize = "vertical" else css.display = "none" end r = mw.html.create( "pre" ) :addClass( support ) :css( css ) :wikitext( mw.text.encode( Data.source ) ) r = tostring( r ) end r = "\n".. r else r = "" end return r
end -- fancy()
local function faraway( alternatives )
-- Retrieve best language version from multilingual text
-- Parameter:
-- alternatives -- table, to be evaluated
-- Returns
-- 1 -- string, with best match
-- 2 -- table of other versions, if any
local n = 0
local variants = { }
local r1, r2
for k, v in pairs( alternatives ) do
if type( v ) == "string" then
v = mw.text.trim( v )
if v ~= "" then
k = k:lower()
variants[ k ] = v
n = n + 1
end
end
end -- for k, v
if n > 0 then
local Multilingual = Fetch( "Multilingual", true )
if Multilingual and
type( Multilingual.i18n ) == "function" then
local show, slang = Multilingual.i18n( variants )
if show then
r1 = show
variants[ slang ] = nil
r2 = variants
end
end
if not r1 then
Foreign()
for k, v in pairs( variants ) do
if n == 1 then
r1 = v
elseif Data.slang == k then
variants[ k ] = nil
r1 = v
r2 = variants
end
end -- for k, v
end
if r2 and Multilingual then
for k, v in pairs( r2 ) do
if v and not Multilingual.isLang( k, true ) then
Fault( string.format( "%s lang=%s
",
"Invalid",
k ) )
end
end -- for k, v
end
end
return r1, r2
end -- faraway()
local function fashioned( about, asked, assign )
-- Create description head -- Parameter: -- about -- table, supposed to contain description -- asked -- true, if mandatory description -- assign -- <block>, if to be equipped -- Returns <block>, with head, or nil local para = assign or mw.html.create( "div" ) local plus, r if about and about.description then if type( about.description ) == "string" then para:wikitext( about.description ) else para:wikitext( about.description[ 1 ] ) plus = mw.html.create( "ul" ) plus:css( "text-align", "left" ) for k, v in pairs( about.description[ 2 ] ) do plus:node( mw.html.create( "li" ) :node( mw.html.create( "code" ) :wikitext( k ) ) :node( mw.html.create( "br" ) ) :wikitext( fair( v ) ) ) end -- for k, v if Config.loudly then plus = mw.html.create( "div" ) :css( "background-color", "#" .. Config.debugmultilang ) :node( plus ) else plus:addClass( "templatedata-maintain" ) :css( "display", "none" ) end end elseif Config.solo and asked then para:addClass( "error" ) :wikitext( Config.solo ) Data.less = true else para = false end if para then if plus then r = mw.html.create( "div" ) :node( para ) :node( plus ) else r = para end end return r
end -- fashioned()
local function fatten( access )
-- Create table row for sub-headline -- Parameter: -- access -- string, with name-- Returns local param = Data.tree.params[ access ] local sub, sort = access:match( "(=+)%s*(%S.*)$" ) local headline = mw.html.create( string.format( "h%d", #sub ) ) local r = mw.html.create( "tr" ) local td = mw.html.create( "td" ) :attr( "colspan", "5" ) :attr( "data-sort-value", "!" .. sort ) local s if param.style then s = type( param.style ) if s == "table" then td:css( param.style ) elseif s == "string" then td:cssText( param.style ) end end s = fashioned( param, false, headline ) if s then headline = s else headline:wikitext( sort ) end td:node( headline ) r:node( td ) return r end -- fatten() local function fathers() -- Merge params with inherited values local n = 0 local p = Data.params local t = Data.tree.params local p2, t2 for k, v in pairs( Data.heirs ) do n = n + 1 end -- for k, v for i = 1, n do if Data.heirs then for k, v in pairs( Data.heirs ) do if v and not Data.heirs[ v ] then n = n - 1 t[ k ].inherits = nil Data.heirs[ k ] = nil p2 = { } t2 = { } if p[ v ] then for k2, v2 in pairs( p[ v ] ) do p2[ k2 ] = v2 end -- for k2, v2 if p[ k ] then for k2, v2 in pairs( p[ k ] ) do if type( v2 ) ~= "nil" then p2[ k2 ] = v2 end end -- for k2, v2 end p[ k ] = p2 for k2, v2 in pairs( t[ v ] ) do t2[ k2 ] = v2 end -- for k2, v2 for k2, v2 in pairs( t[ k ] ) do if type( v2 ) ~= "nil" then t2[ k2 ] = v2 end end -- for k2, v2 t[ k ] = t2 else Fault( "No params[] inherits " .. v ) end end end -- for k, v end end -- i = 1, n if n > 0 then local s for k, v in pairs( Data.heirs ) do if v then if s then s = string.format( "%s | %s", s, k ) else s = "Circular inherits: " .. k end end end -- for k, v Fault( s ) end end -- fathers() local function favorize() -- Local customization issues local boole = { ["font-size"] = "125%" } local l, cx = pcall( mw.loadData, TemplateData.frame:getTitle() .. "/config" ) local scripting TemplateData.ltr = not mw.language.getContentLanguage():isRTL() if TemplateData.ltr then scripting = "left" else scripting = "right" end boole[ "margin-" .. scripting ] = "3em" Permit.boole = { [false] = { css = boole, lead = true, show = "☐" }, [true] = { css = boole, lead = true, show = "☑" } } Permit.css = { } for k, v in pairs( Permit.colors ) do if k == "tableheadbg" then k = "tablehead" end Permit.css[ k ] = { ["background-color"] = "#" .. v } end -- for k, v if type( cx ) == "table" then local c, s if type( cx.permit ) == "table" then if type( cx.permit.boole ) == "table" then if type( cx.permit.boole[ true ] ) == "table" then Permit.boole[ false ] = cx.permit.boole[ false ] end if type( cx.permit.boole[ true ] ) == "table" then Permit.boole[ true ] = cx.permit.boole[ true ] end end if type( cx.permit.css ) == "table" then for k, v in pairs( cx.permit.css ) do if type( v ) == "table" then Permit.css[ k ] = v end end -- for k, v end end for k, v in pairs( Config.basicCnf ) do s = type( cx[ k ] ) if s == "string" or s == "table" then Config[ v ] = cx[ k ] end end -- for k, v end if type( Config.subpage ) ~= "string" or type( Config.suffix ) ~= "string" then local got = mw.message.new( "templatedata-doc-subpage" ) local suffix if got:isDisabled() then suffix = "doc" else suffix = got:plain() end if type( Config.subpage ) ~= "string" then Config.subpage = string.format( "/%s$", suffix ) end if type( Config.suffix ) ~= "string" then Config.suffix = string.format( "%%s/%s", suffix ) end end end -- favorize() local function feat() -- Check and store parameter sequence if Data.source then local i = 0 local s for k, v in pairs( Data.tree.params ) do if i == 0 then Data.order = { } i = 1 s = k else i = 2 break -- for k, v end end -- for k, v if i > 1 then local pointers = { } local points = { } local given = { } for k, v in pairs( Data.tree.params ) do i = facet( k, 1 ) if type( v ) == "table" then if type( v.label ) == "string" then s = mw.text.trim( v.label ) if s == "" then s = k end else s = k end if given[ s ] then if given[ s ] == 1 then local scream = "Parameter label '%s' detected multiple times" Fault( string.format( scream, s ) ) given[ s ] = 2 end else given[ s ] = 1 end end if i then table.insert( points, i ) pointers[ i ] = k i = facet( k, i ) if i then s = "Parameter '%s' detected twice" Fault( string.format( s, k ) ) end else s = "Parameter '%s' not detected" Fault( string.format( s, k ) ) end end -- for k, v table.sort( points ) for i = 1, #points do table.insert( Data.order, pointers[ points[ i ] ] ) end -- i = 1, #points elseif s then table.insert( Data.order, s ) end end end -- feat() local function feature( access ) -- Create table row for parameter, check and display violations -- Parameter: -- access -- string, with name -- Returns local mode, s, status local fine = function ( a ) s = mw.text.trim( a ) return a == s and a ~= "" and not a:find( "%|=\n" ) and not a:find( "%s%s" ) end local begin = mw.html.create( "td" ) local code = mw.html.create( "code" ) local desc = mw.html.create( "td" ) local legal = true local param = Data.tree.params[ access ] local ranking = { "required", "suggested", "optional", "deprecated" } local r = mw.html.create( "tr" ) local sort, typed for k, v in pairs( param ) do if v == "" then param[ k ] = false end end -- for k, v -- label sort = param.label or access if sort:match( "^%d+$" ) then begin:attr( "data-sort-value", string.format( "%05d", tonumber( sort ) ) ) end begin:css( "font-weight", "bold" ) :wikitext( sort ) -- name and aliases code:css( "font-size", "92%" ) :css( "white-space", "nowrap" ) :wikitext( access ) if not fine( access ) then code:addClass( "error" ) Fault( string.format( "Bad ID params.
%s
", access ) )
legal = false
begin:attr( "data-sort-value", " " .. sort )
end
code = mw.html.create( "td" )
:node( code )
if access:match( "^%d+$" ) then
code:attr( "data-sort-value",
string.format( "%05d", tonumber( access ) ) )
end
if type( param.aliases ) == "table" then
local lapsus
for k, v in pairs( param.aliases ) do
code:tag( "br" )
if type( v ) == "string" then
if not fine( v ) then
lapsus = true
code:node( mw.html.create( "span" )
:addClass( "error" )
:css( "font-style", "italic" )
:wikitext( "string" ) )
end
code:wikitext( s )
else
lapsus = true
code:node( mw.html.create( "code" )
:addClass( "error" )
:wikitext( type( v ) ) )
end
end -- for k, v
if lapsus then
s = string.format( "params.%s
.aliases", access )
Fault( factory( "invalid-value" ):gsub( "$1", s ) )
legal = false
end
end
-- description etc.
s = fashioned( param )
if s then
desc:node( s )
end
if param.style then
s = type( param.style )
if s == "table" then
desc:css( param.style )
elseif s == "string" then
desc:cssText( param.style )
end
end
if param.default or param.example or param.autovalue then
local details = { "default", "example", "autovalue" }
local dl = mw.html.create( "dl" )
local dd, section, show
for i = 1, #details do
s = details[ i ]
show = param[ s ]
if show then
dd = mw.html.create( "dd" )
section = factory( "doc-param-" .. s )
if param.type == "boolean" and
( show == "0" or show == "1" ) then
local boole = Permit.boole[ ( show == "1" ) ]
if boole.lead == true then
dd:node( mw.html.create( "code" )
:wikitext( show ) )
:wikitext( " " )
end
if type( boole.show ) == "string" then
local v = mw.html.create( "span" )
:wikitext( boole.show )
if boole.css then
v:css( boole.css )
end
dd:node( v )
end
if type( boole.suffix ) == "string" then
dd:wikitext( boole.suffix )
end
if boole.lead == false then
dd:wikitext( " " )
:node( mw.html.create( "code" )
:wikitext( show ) )
end
else
dd:wikitext( show )
end
dl:node( mw.html.create( "dt" )
:wikitext( section ) )
:node( dd )
end
end -- i = 1, #details
desc:node( dl )
end
-- type
if type( param.type ) == "string" then
param.type = mw.text.trim( param.type )
if param.type == "" then
param.type = false
end
end
if param.type then
s = Permit.types[ param.type ]
typed = mw.html.create( "td" )
if s then
if s == "string" then
Data.params[ access ].type = s
typed:wikitext( factory( "doc-param-type-" .. s ) )
:tag( "br" )
typed:node( mw.html.create( "span" )
:addClass( "error" )
:wikitext( param.type ) )
Data.lasting = true
else
local support = Config[ "support4" .. param.type ]
s = factory( "doc-param-type-" .. param.type )
if support then
s = string.format( "%s", support, s )
end
typed:wikitext( s )
end
else
Data.params[ access ].type = "unknown"
typed:addClass( "error" )
:wikitext( "INVALID" )
s = string.format( "params.%s
.type", access )
Fault( factory( "invalid-value" ):gsub( "$1", s ) )
legal = false
end
else
typed = mw.html.create( "td" )
:wikitext( factory( "doc-param-type-unknown" ) )
end
-- status
if param.required then
mode = 1
if param.autovalue then
Fault( string.format( "autovalued %s
required",
access ) )
legal = false
end
if param.default then
Fault( string.format( "Defaulted %s
required",
access ) )
legal = false
end
if param.deprecated then
Fault( string.format( "Required deprecated %s
",
access ) )
legal = false
end
elseif param.deprecated then
mode = 4
elseif param.suggested then
mode = 2
else
mode = 3
end
status = ranking[ mode ]
ranking = factory( "doc-param-status-" .. status )
if mode == 1 or mode == 4 then
ranking = mw.html.create( "span" )
:css( "font-weight", "bold" )
:wikitext( ranking )
if type( param.deprecated ) == "string" then
ranking:tag( "br" )
ranking:wikitext( param.deprecated )
end
end
--
r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) )
:css( Permit.css[ status ] )
:node( begin )
:node( code )
:node( desc )
:node( typed )
:node( mw.html.create( "td" )
:attr( "data-sort-value", tostring( mode ) )
:node( ranking ) )
:newline()
if not legal then
r:css( "border", "#FF0000 3px solid" )
end
return r
end -- feature()
local function features()
-- Create for parameters
-- Returns %s
",
s ) )
else
tbl:node( feature( s ) )
end
end -- for i = 1, #Data.order
if leave then
for i = #Data.order, 1, -1 do
if not Data.order[ i ] then
table.remove( Data.order, i )
end
end -- for i = #Data.order, 1, -1
end
Data.tag.paramOrder = Data.order
end
if Config.cssTabWrap or Data.scroll then
r = mw.html.create( "div" )
if type( Config.cssTabWrap ) == "table" then
r:css( Config.cssTabWrap )
elseif type( Config.cssTabWrap ) == "string" then
-- deprecated
r:cssText( Config.cssTabWrap )
end
if Data.scroll then
r:css( "height", Data.scroll )
:css( "overflow", "auto" )
end
r:node( tbl )
else
r = tbl
end
end
return r
end -- features()
local function finalize( advance )
-- Wrap presentation into frame
-- Parameter:
-- advance -- true, for nice
-- Returns string
local r, lapsus
if Data.div then
r = tostring( Data.div )
elseif Data.strip then
r = Data.strip
else
lapsus = true
r = ""
end
r = r .. failures()
if Data.source then
local live = ( advance or lapsus )
if not live then
live = TemplateData.frame:preprocess( "4580" )
live = ( live == "" )
end
if live then
r = r .. fancy( advance, lapsus )
end
end
return r
end -- finalize()
local function find()
-- Find JSON data within page source (title)
-- Returns string, or nil
local s = Data.title:getContent()
local i, j = s:find( "<templatedata>", 1, true )
local r
if i then
local k = s:find( "</templatedata>", j, true )
if k then
r = mw.text.trim( s:sub( j + 1, k - 1 ) )
end
end
return r
end -- find()
local function flat( adjust )
-- Remove formatting from text string
-- Parameter:
-- arglist -- string, to be stripped, or nil
-- Returns string, or nil
local r
if adjust then
r = adjust:gsub( "\n", " " )
if r:find( "<noexport>", 1, true ) then
r = r:gsub( "<noexport>(.*)</noexport>", "" )
end
if r:find( "", 1, true ) then
r = r:gsub( "'", "" ):gsub( "", "" )
end
if r:find( "<", 1, true ) then
local Text = Fetch( "Text" )
r = Text.getPlain( r:gsub( "", "\r\n" ) ) end if r:find( "[", 1, true ) then local WLink = Fetch( "WLink" ) if WLink.isBracketedURL( r ) then r = r:gsub( "%[([hf]tt?ps?://%S+) [^%]]+%]", "%1" ) end r = WLink.getPlain( r ) end if r:find( "&", 1, true ) then r = mw.text.decode( r ) end end return r end -- flat() local function flush() -- JSON encode narrowed input; obey unnamed (numerical) parameters -- Returns <templatedata> JSON string local r if Data.tag then r = mw.text.jsonEncode( Data.tag ):gsub( "%}$", "," ) else r = "{" end r = r .. "\n\"params\":{" if Data.order then local sep = "" local s for i = 1, #Data.order do s = Data.order[ i ] r = string.format( "%s%s\n%s:%s", r, sep, mw.text.jsonEncode( s ), mw.text.jsonEncode( Data.params[ s ] ) ) sep = ",\n" end -- for i = 1, #Data.order end r = r .. "\n}\n}" return r end -- flush() local function focus( access ) -- Check components; focus multilingual description, build trees -- Parameter: -- access -- string, name of parameter, nil for root local f = function ( a, at ) local r if at then r = string.format( "
params.%s
", at )
else
r = "root"
end
if a then
r = string.format( "%s.%s
", r, a )
end
return r
end
local parent
if access then
parent = Data.got.params[ access ]
else
parent = Data.got
end
if type( parent ) == "table" then
local elem, got, permit, s, scope, slot, tag, target
if access then
permit = Permit.params
if type( access ) == "number" then
slot = tostring( access )
else
slot = access
end
else
permit = Permit.root
end
for k, v in pairs( parent ) do
scope = permit[ k ]
if scope then
s = type( v )
if s == "string" and k ~= "format" then
v = mw.text.trim( v )
end
if scope:find( s, 1, true ) then
if scope:find( "I18N", 1, true ) then
if s == "string" then
elem = fair( v )
elseif s == "table" then
local translated
v, translated = faraway( v )
if v then
if translated and
k == "description" then
elem = { [ 1 ] = fair( v ),
[ 2 ] = translated }
else
elem = fair( v )
end
else
elem = false
end
end
if type( v ) == "string" then
if k == "deprecated" then
if v == "1" then
v = true
elseif v == "0" then
v = false
end
elem = v
elseif scope:find( "nowiki", 1, true ) then
elem = mw.text.nowiki( v )
elem = elem:gsub( " \n", "" ) v = v:gsub( string.char( 13 ), "" ) else v = flat( v ) end elseif s == "boolean" then if scope:find( "boolean", 1, true ) then elem = v else s = "Type
boolean
bad for "
.. f( k, slot )
Fault( s )
end
end
else
if k == "params" and not access then
v = nil
elem = nil
elseif k == "format" and not access then
elem = mw.text.decode( v )
v = nil
elseif k == "inherits" then
elem = v
if not Data.heirs then
Data.heirs = { }
end
Data.heirs[ slot ] = v
v = nil
elseif k == "style" then
elem = v
v = nil
elseif s == "string" then
v = mw.text.nowiki( v )
elem = v
else
elem = v
end
end
if type( elem ) ~= "nil" then
if not target then
if access then
if not Data.tree.params then
Data.tree.params = { }
end
Data.tree.params[ slot ] = { }
target = Data.tree.params[ slot ]
else
Data.tree = { }
target = Data.tree
end
end
target[ k ] = elem
elem = false
end
if type( v ) ~= "nil" then
if not tag then
if access then
if type( v ) == "string" and
v.sub( 1, 1 ) == "=" then
v = nil
else
if not Data.params then
Data.params = { }
end
Data.params[ slot ] = { }
tag = Data.params[ slot ]
end
else
Data.tag = { }
tag = Data.tag
end
end
if type( v ) ~= "nil" then
tag[ k ] = v
end
end
else
s = string.format( "Type %s
bad for %s",
scope, f( k, slot ) )
Fault( s )
end
else
Fault( "Unknown component " .. f( k, slot ) )
end
end -- for k, v
else
Fault( f() .. " needs to be of object
type" )
end
end -- focus()
local function format()
-- Build formatted element
-- Returns <inline>
local source = Data.tree.format:lower()
local r, s
if source == "inline" or source == "block" then
r = mw.html.create( "i" )
:wikitext( source )
else
local code
if source:find( "|", 1, true ) then
local scan = "^[\n ]*%{%{[\n _]*|[\n _]*=[\n _]*%}%}[\n ]*$"
if source:match( scan ) then
code = source:gsub( "\n", "N" )
else
s = mw.text.nowiki( source ):gsub( "\n", "\n" )
s = tostring( mw.html.create( "code" )
:wikitext( s ) )
Fault( "Invalid format " .. s )
source = false
end
else
local words = mw.text.split( source, "%s+" )
local show, start, support, unknown
for i = 1, #words do
s = words[ i ]
if i == 1 then
start = s
end
support = Permit.builder[ s ]
if support == start or
support == "*" then
Permit.builder[ s ] = true
elseif s:match( "^[1-9]%d?" ) and
Permit.builder.align then
Permit.builder.align = tonumber( s )
else
if unknown then
unknown = string.format( "%s %s", unknown, s )
else
unknown = s
end
end
end -- i = 1, #words
if unknown then
s = tostring( mw.html.create( "code" )
:css( "white-space", "nowrap" )
:wikitext( s ) )
Fault( "Unknown/misplaced format keyword " .. s )
source = false
start = false
end
if start == "inline" then
if Permit.builder.half == true then
show = "inline half"
code = "{{_ |_=_}}"
elseif Permit.builder.grouped == true then
show = "inline grouped"
code = "{{_ | _=_}}"
elseif Permit.builder.spaced == true then
show = "inline spaced"
code = "{{_ | _ = _ }}"
end
if Permit.builder.newlines == true then
show = show or "inline"
code = code or "{{_|_=_}}"
show = show .. " newlines"
code = string.format( "N%sN", code )
end
elseif start == "block" then
local space = "" -- amid "|" and name
local spaced = " " -- preceding "="
local spacer = " " -- following "="
local suffix = "N" -- closing "}}" on new line
show = "block"
if Permit.builder.indent == true then
start = " "
show = "block indent"
else
start = ""
end
if Permit.builder.compressed == true then
spaced = ""
spacer = ""
show = show .. " compressed"
if Permit.builder.last == true then
show = show .. " last"
else
suffix = ""
end
else
if Permit.builder.lead == true then
show = show .. " lead"
space = " "
end
if type( Permit.builder.align ) ~= "string" then
local n
s = " align"
if Permit.builder.align == true then
n = 0
if type( Data.got ) == "table" and
type( Data.got.params ) == "table" then
for k, v in pairs( Data.got.params ) do
if type( v ) == "table" and
not v.deprecated and
type( k ) == "string" then
k = mw.ustring.len( k )
if k > n then
n = k
end
end
end -- for k, v
end
else
n = Permit.builder.align
if type( n ) == "number" and n > 1 then
s = string.format( "%s %d", s, n )
else
n = 0 -- How comes?
end
end
if n > 1 then
spaced = string.rep( "_", n ) .. " "
end
show = show .. s
elseif Permit.builder.after == true then
spaced = ""
show = show .. " after"
elseif Permit.builder.dense == true then
spaced = ""
spacer = ""
show = show .. " dense"
end
if Permit.builder.last == true then
suffix = spacer
show = show .. " last"
end
end
code = string.format( "NVorlage:N%sN",
start,
space,
spaced,
spacer,
suffix )
if show == "block" then
show = "block newlines"
end
end
if show then
r = mw.html.create( "span" )
:wikitext( show )
end
end
if code then
source = code:gsub( "N", "\n" )
code = mw.text.nowiki( code ):gsub( "N", "\n" )
code = mw.html.create( "code" )
:css( "margin-left", "1em" )
:css( "margin-right", "1em" )
:wikitext( code )
if r then
r = mw.html.create( "span" )
:node( r )
:node( code )
else
r = code
end
end
end
if source then
Data.tag.format = source
end
return r
end -- format()
local function formatter()
-- Build presented documentation
-- Returns local r = mw.html.create( "div" ) local x = fashioned( Data.tree, true, r ) local s if x then r = x end if Data.leading then local toc = mw.html.create( "div" ) local shift if Config.suppressTOCnum then toc:addClass( Config.suppressTOCnum ) if type( Config.stylesTOCnum ) == "string" then local src = Config.stylesTOCnum .. "/styles.css" s = TemplateData.frame:extensionTag( "templatestyles", nil, { src = src } ) r:newline() :node( s ) end end toc:css( "margin-top", "0.5em" ) :wikitext( "" ) if Data.sibling then local block = mw.html.create( "div" ) if TemplateData.ltr then shift = "right" else shift = "left" end block:css( "float", shift ) :wikitext( Data.sibling ) r:newline() :node( block ) :newline() end r:newline() :node( toc ) :newline() if shift then r:node( mw.html.create( "div" ) :css( "clear", shift ) ) :newline() end end s = features() if s then if Data.leading then r:node( mw.html.create( "h2" ) :wikitext( factory( "doc-params" ) ) ) :newline() end r:node( s ) end if Data.shared then local global = mw.html.create( "div" ) :attr( "id", "templatedata-global" ) local shift if TemplateData.ltr then shift = "right" else shift = "left" end global:css( "float", shift ) :wikitext( string.format( "%s", Data.shared, "Global" ) ) r:newline() :node( global ) end if Data.tree and Data.tree.format then local e = format() if e then local show = "Format" if Config.supportFormat then show = string.format( "%s", Config.supportFormat, show ) end r:node( mw.html.create( "p" ) :wikitext( show .. ": " ) :node( e ) ) end end return r
end -- formatter()
local function free()
-- Remove JSON comment lines Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([},\"'])", "%1%3" )
end -- free()
local function full()
-- Build survey table from JSON data, append invisible <templatedata> Data.div = mw.html.create( "div" ) :addClass( "mw-templatedata-doc-wrap" ) focus() if Data.tag then if type( Data.got.params ) == "table" then for k, v in pairs( Data.got.params ) do focus( k ) end -- for k, v if Data.heirs then fathers() end end end Data.div:node( formatter() ) if not Data.lazy then Data.slim = flush() if TemplateData.frame then local div = mw.html.create( "div" ) local tdata = { [ 1 ] = "templatedata", [ 2 ] = Data.slim } Data.strip = TemplateData.frame:callParserFunction( "#tag", tdata ) div:wikitext( Data.strip ) if Config.loudly then Data.div:node( mw.html.create( "hr" ) :css( { height = "7ex" } ) ) else div:css( "display", "none" ) end Data.div:node( div ) end end if Data.lasting then Fault( "deprecated type syntax" ) end if Data.less then Fault( Config.solo ) end
end -- full()
local function furnish( adapt, arglist )
-- Analyze transclusion -- Parameter: -- adapt -- table, #invoke parameters -- arglist -- table, template parameters -- Returns string local source favorize() -- deprecated: for k, v in pairs( Config.basicCnf ) do if adapt[ k ] and adapt[ k ] ~= "" then Config[ v ] = adapt[ k ] end end -- for k, v Config.loudly = faculty( arglist.debug or adapt.debug ) Data.lazy = faculty( arglist.lazy ) and not Config.loudly Data.leading = faculty( arglist.TOC ) if Data.leading and arglist.TOCsibling then Data.sibling = mw.text.trim( arglist.TOCsibling ) end if arglist.lang then Data.slang = arglist.lang:lower() elseif adapt.lang then Data.slang = adapt.lang:lower() end if arglist.JSON then source = arglist.JSON elseif arglist.Global then source = TemplateData.getGlobalJSON( arglist.Global, arglist.Local ) elseif arglist[ 1 ] then local s = mw.text.trim( arglist[ 1 ] ) local start = s:sub( 1, 1 ) if start == "<" then Data.strip = s elseif start == "{" then source = s elseif mw.ustring.sub( s, 1, 8 ) == mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then Data.strip = s end end if type( arglist.vertical ) == "string" and arglist.vertical:match( "^%d*%.?%d+[emprx]+$" ) then Data.scroll = arglist.vertical end if not source then Data.title = mw.title.getCurrentTitle() source = find() if not source and not Data.title.text:match( Config.subpage ) then local s = string.format( Config.suffix, Data.title.prefixedText ) Data.title = mw.title.new( s ) if Data.title.exists then source = find() end end end if not Data.lazy then if not Data.title then Data.title = mw.title.getCurrentTitle() end Data.lazy = Data.title.text:match( Config.subpage ) end if type( source ) == "string" then TemplateData.getPlainJSON( source ) end return finalize( faculty( arglist.source ) )
end -- furnish()
TemplateData.failsafe = function ( atleast )
-- Retrieve versioning and check for compliance -- Precondition: -- atleast -- string, with required version or "wikidata" -- or false -- Postcondition: -- Returns string with appropriate version, or false local since = atleast local r if since == "wikidata" then local item = TemplateData.item since = false if type( item ) == "number" and item > 0 then local entity = mw.wikibase.getEntity( string.format( "Q%d", item ) ) if type( entity ) == "table" then local vsn = entity:formatPropertyValues( "P348" ) if type( vsn ) == "table" and type( vsn.value ) == "string" and vsn.value ~= "" then r = vsn.value end end end end if not r then if not since or since <= TemplateData.serial then r = TemplateData.serial else r = false end end return r
end -- TemplateData.failsafe()
TemplateData.getGlobalJSON = function ( access, adapt )
-- Retrieve TemplateData from a global repository (JSON) -- Parameter: -- access -- string, with page specifier (on WikiMedia Commons) -- adapt -- JSON string or table with local overrides -- Returns true, if succeeded local plugin = Fetch( "/global" ) local r if type( plugin ) == "table" and type( plugin.fetch ) == "function" then local s, got = plugin.fetch( access, adapt ) if got then Data.got = got Data.order = got.paramOrder Data.shared = s r = true full() else Fault( s ) end end return r
end -- TemplateData.getGlobalJSON()
TemplateData.getPlainJSON = function ( adapt )
-- Reduce enhanced JSON data to plain text localized JSON -- Parameter: -- adapt -- string, with enhanced JSON -- Returns string, or not if type( adapt ) == "string" then local JSONutil = Fetch( "JSONutil", true ) Data.source = adapt free() if JSONutil then local Multilingual = Fetch( "Multilingual", true ) local f if Multilingual then f = Multilingual.i18n end Data.got = JSONutil.fetch( Data.source, true, f ) else local lucky lucky, Data.got = pcall( mw.text.jsonDecode, Data.source ) end if type( Data.got ) == "table" then full() elseif not Data.strip then Fault( "fatal JSON error: " .. Data.got ) end end return Data.slim
end -- TemplateData.getPlainJSON()
TemplateData.test = function ( adapt, arglist )
TemplateData.frame = mw.getCurrentFrame() return furnish( adapt, arglist )
end -- TemplateData.test()
-- Export local p = { }
p.f = function ( frame )
-- Template call local lucky, r TemplateData.frame = frame lucky, r = pcall( furnish, frame.args, frame:getParent().args ) if not lucky then Fault( "INTERNAL: " .. r ) r = failures() end return r
end -- p.f()
p.failsafe = function ( frame )
-- Versioning interface local s = type( frame ) local since if s == "table" then since = frame.args[ 1 ] elseif s == "string" then since = frame end if since then since = mw.text.trim( since ) if since == "" then since = false end end return TemplateData.failsafe( since ) or ""
end -- p.failsafe()
p.TemplateData = function ()
-- Module interface return TemplateData
end
return p