Module:Format TemplateData: Difference between revisions
From Zoophilia Wiki
Jump to navigationJump to search
de>PerfektesChaos (2017-03-28) |
m (38 revisions imported) |
||
(35 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
local TemplateData = { serial = "2017- | local TemplateData = { serial = "2017-11-06", | ||
suite = "TemplateData" } | suite = "TemplateData" } | ||
local plaintext = require("Module:Plain text") | |||
--[=[ | --[=[ | ||
improve template:TemplateData | improve template:TemplateData | ||
]=] | ]=] | ||
local Config = { | local Config = { | ||
-- multiple #invoke option names mapped into unique internal fields | -- multiple #invoke option names mapped into unique internal fields | ||
cat | cat = "strange", | ||
debug | classNoNumTOC = "suppressTOCnum", | ||
docpageCreate = "suffix", | -- classParams = "classTable", | ||
docpageDetect = "subpage", | cssParams = "stylesTable", | ||
msgDescMiss | cssParWrap = "stylesTabWrap", | ||
loudly | debug = false, | ||
solo | docpageCreate = "suffix", | ||
strange | docpageDetect = "subpage", | ||
subpage | msgDescMiss = "solo", | ||
suffix | -- classTable = false, -- class for params table | ||
loudly = false, -- show exported element, etc. | |||
solo = false, -- complaint on missing description | |||
strange = false, -- title of maintenance category | |||
stylesTable = false, -- styles for params table | |||
stylesTabWrap = false, -- styles for params table wrapper | |||
subpage = false, -- pattern to identify subpage | |||
suffix = false, -- subpage creation scheme | |||
suppressTOCnum = false -- class for TOC number suppression | |||
} | } | ||
local Data = { | local Data = { | ||
Line 26: | Line 32: | ||
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 | ||
Line 39: | Line 47: | ||
} | } | ||
local Permit = { | local Permit = { | ||
styles = { required = "border-left: 3px solid black;", | |||
suggested | suggested = "border-left: 3px solid #888;", | ||
optional | optional = "border-left: 3px solid #ccc", | ||
deprecated = " | deprecated = "border-left: 3px dotted red; background-color: #FDD;", | ||
tableheadbg = "background-color: #B3B7FF;" }, | |||
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", | ||
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", | ||
Line 79: | Line 88: | ||
} | } | ||
-- | |||
-- Generic utility functions | |||
-- | |||
local function Fault( alert ) | local function Fault( alert ) | ||
Line 86: | Line 97: | ||
-- 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 | ||
Line 92: | Line 103: | ||
end -- Fault() | end -- Fault() | ||
local function collapseWhitespace ( a ) | |||
-- Collapses whitespace, HTML style. | |||
return a:gsub( "%s*\n%s*", " " ) | |||
:gsub( "%s%s+", " " ) | |||
end -- collapseWhitespace | |||
-------------------------------------------------------------------------------- | |||
-- | |||
local function facet( ask, at ) | local function facet( ask, at ) | ||
Line 102: | Line 120: | ||
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 | ||
Line 123: | Line 142: | ||
local function | local function getLocalizedText( adapt ) | ||
-- Retrieve localized text from system message | -- Retrieve localized text from system message | ||
-- Parameter: | -- Parameter: | ||
Line 174: | Line 148: | ||
-- Returns string, with localized text | -- Returns string, with localized text | ||
return mw.message.new( "templatedata-" .. adapt ):plain() | return mw.message.new( "templatedata-" .. adapt ):plain() | ||
end -- | end -- getLocalizedText() | ||
Line 217: | Line 191: | ||
return r | return r | ||
end -- failures() | end -- failures() | ||
local function handleNoexportWhitespace( adjust ) | |||
-- Reduces runs of spaces, including newlines, to a single space, so the | |||
-- whole string is on one line. <noexport> blocks are left alone, but the | |||
-- <noexport> tags themselves are removed. | |||
-- adjust -- string | |||
-- Returns string, with adjusted text | |||
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 .. collapseWhitespace( 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 = collapseWhitespace( adjust ) | |||
end | |||
return r | |||
end -- handleNoexportWhitespace() | |||
Line 228: | Line 236: | ||
-- 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 | ||
if not Data.slang then | if not Data.slang then | ||
Line 235: | Line 244: | ||
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 | ||
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( | for k, v in pairs( variants ) do | ||
if v then | if v then | ||
if n == 1 then | if n == 1 then | ||
r1 = v | r1 = v | ||
elseif k:lower() == Data.slang then | elseif k:lower() == Data.slang then | ||
variants[ k ] = nil | |||
r1 = v | r1 = v | ||
r2 = | r2 = variants | ||
break -- for k, v | break -- for k, v | ||
end | end | ||
Line 259: | Line 265: | ||
if not r1 then | if not r1 then | ||
local seek = string.format( "^%s-", Data.slang ) | local seek = string.format( "^%s-", Data.slang ) | ||
for k, v in pairs( | for k, v in pairs( variants ) do | ||
if v and k:lower():match( seek ) then | if v and k:lower():match( seek ) then | ||
variants[ k ] = nil | |||
r1 = v | r1 = v | ||
r2 = | r2 = variants | ||
break -- for k, v | break -- for k, v | ||
end | end | ||
Line 272: | Line 278: | ||
for i = 1, #others do | for i = 1, #others do | ||
seek = others[ i ] | seek = others[ i ] | ||
if | if variants[ seek ] then | ||
r1 = | r1 = variants[ seek ] | ||
variants[ seek ] = nil | |||
r2 = | r2 = variants | ||
break -- for i | break -- for i | ||
end | end | ||
Line 281: | Line 287: | ||
end | end | ||
if not r1 then | if not r1 then | ||
for k, v in pairs( | for k, v in pairs( variants ) do | ||
if v then | if v then | ||
variants[ k ] = nil | |||
r1 = v | r1 = v | ||
r2 = | r2 = variants | ||
break -- for k, v | break -- for k, v | ||
end | end | ||
Line 292: | Line 298: | ||
end | end | ||
if r2 then | if r2 then | ||
for k, v in pairs( r2 ) do | for k, v in pairs( r2 ) do | ||
if v | if v then | ||
local baseCode = k:match( "^%s*(%a%a%a?)-?%a*%s*$" ) | |||
if not baseCode or not mw.language.isKnownLanguageTag( baseCode ) then | |||
Fault( string.format( "Invalid <code>lang=%s</code>", k )) | |||
end | |||
end | end | ||
end -- for k, v | end -- for k, v | ||
Line 326: | Line 333: | ||
p2[ k2 ] = v2 | p2[ k2 ] = v2 | ||
end -- for k2, v2 | end -- for k2, v2 | ||
for k2, v2 in pairs( p[ k ] ) do | 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 | p[ k ] = p2 | ||
for k2, v2 in pairs( t[ v ] ) do | for k2, v2 in pairs( t[ v ] ) do | ||
Line 358: | Line 367: | ||
end | end | ||
end -- fathers() | end -- fathers() | ||
local function feasible( about, asked ) | |||
-- Create description head | |||
-- Parameter: | |||
-- about -- table, supposed to contain description | |||
-- asked -- true, if mandatory description | |||
-- Returns <block>, with head, or nil | |||
local para = 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" ) | |||
if not Config.loudly then | |||
plus:addClass( "templatedata-maintain" ) | |||
:css( "display", "none" ) | |||
end | |||
for k, v in pairs( about.description[ 2 ] ) do | |||
plus:node( mw.html.create( "li" ) | |||
:node( mw.html.create( "code" ) | |||
:wikitext( k ) ) | |||
:node( mw.html.create( "br" ) ) | |||
:wikitext( handleNoexportWhitespace( v ) ) ) | |||
end -- for k, v | |||
end | |||
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 -- feasible() | |||
Line 444: | Line 500: | ||
-- 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" ) | ||
Line 479: | Line 537: | ||
if lapsus then | if lapsus then | ||
s = string.format( "params.<code>%s</code>.aliases", access ) | s = string.format( "params.<code>%s</code>.aliases", access ) | ||
Fault( | Fault( getLocalizedText( "invalid-value" ):gsub( "$1", s ) ) | ||
legal = false | legal = false | ||
end | end | ||
Line 485: | Line 543: | ||
-- description etc. | -- description etc. | ||
s = | s = feasible( param ) | ||
if s then | if s then | ||
desc:node( s ) | desc:node( s ) | ||
Line 492: | Line 550: | ||
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 | ||
section = | section = getLocalizedText( "doc-param-" .. s ) | ||
dt = mw.html.create( "dt" ):wikitext( section ) | |||
dd = mw.html.create( "dd" ) | |||
if (string.len(show) < 80) then | |||
dt:cssText("float: left;margin-right: 1.6em;") | |||
end | |||
if param.type == "boolean" then | |||
if show == "0" then | |||
dd:wikitext("<span style=\"color: #610; font-weight: bold;\">False</span> (<code>0</code>)") | |||
elseif show == "1" then | |||
dd:wikitext("<span style=\"color: #050; font-weight: bold;\">True</span> (<code>1</code>)") | |||
else | |||
dd:wikitext( show ) | |||
end | |||
else | |||
dd:wikitext( show ) | |||
end | |||
dl:node( dt ) | |||
:node( dd ) | |||
end | end | ||
end -- i = 1, #details | end -- i = 1, #details | ||
Line 514: | Line 586: | ||
if s == "string" then | if s == "string" then | ||
Data.params[ access ].type = s | Data.params[ access ].type = s | ||
typed:wikitext( | typed:wikitext( getLocalizedText( "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 | ||
s = | s = getLocalizedText( "doc-param-type-" .. param.type ) | ||
typed:wikitext( s ) | typed:wikitext( s ) | ||
end | end | ||
Line 529: | Line 601: | ||
:wikitext( "INVALID" ) | :wikitext( "INVALID" ) | ||
s = string.format( "params.<code>%s</code>.type", access ) | s = string.format( "params.<code>%s</code>.type", access ) | ||
Fault( | Fault( getLocalizedText( "invalid-value" ):gsub( "$1", s ) ) | ||
legal = false | legal = false | ||
end | end | ||
else | else | ||
typed = mw.html.create( "td" ) | typed = mw.html.create( "td" ) | ||
:wikitext( | :wikitext( getLocalizedText( "doc-param-type-unknown" ) ) | ||
end | end | ||
Line 553: | Line 625: | ||
end | end | ||
status = ranking[ mode ] | status = ranking[ mode ] | ||
ranking = | ranking = getLocalizedText( "doc-param-status-" .. status ) | ||
if mode == 1 or mode == 4 then | if mode == 1 or mode == 4 then | ||
ranking = mw.html.create( "span" ) | ranking = mw.html.create( "span" ) | ||
Line 560: | Line 632: | ||
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 | ||
Line 566: | Line 638: | ||
-- <tr> | -- <tr> | ||
r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) ) | r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) ) | ||
: | :cssText( Permit.styles[ status ] ) | ||
:node( begin ) | :node( begin ) | ||
:node( code ) | :node( code ) | ||
Line 588: | Line 660: | ||
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 style = Permit.styles.tableheadbg | ||
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.stylesTable then | |||
tbl:cssText( Config.stylesTable ) | |||
end | |||
tr:node( mw.html.create( "th" ) | tr:node( mw.html.create( "th" ) | ||
:attr( "colspan", "2" ) | :attr( "colspan", "2" ) | ||
:wikitext( | :cssText( style ) | ||
:wikitext( getLocalizedText( "doc-param-name" ) ) ) | |||
:node( mw.html.create( "th" ) | :node( mw.html.create( "th" ) | ||
:wikitext( | :cssText( style ) | ||
:wikitext( getLocalizedText( "doc-param-desc" ) ) ) | |||
:node( mw.html.create( "th" ) | :node( mw.html.create( "th" ) | ||
:wikitext( | :cssText( style ) | ||
:wikitext( getLocalizedText( "doc-param-type" ) ) ) | |||
:node( mw.html.create( "th" ) | :node( mw.html.create( "th" ) | ||
:wikitext( | :cssText( style ) | ||
:wikitext( getLocalizedText( "doc-param-status" ) ) ) | |||
tbl:newline() | |||
-- :node( mw.html.create( "thead" ) | |||
:node( tr ) | |||
-- | |||
-- ) | -- ) | ||
:newline() | |||
if Data.order then | if Data.order then | ||
for i = 1, #Data.order do | for i = 1, #Data.order do | ||
tbl:node( feature( Data.order[ i ] ) ) | |||
end -- for i = 1, #Data.order | end -- for i = 1, #Data.order | ||
end | end | ||
if Config.stylesTabWrap then | |||
r = mw.html.create( "div" ) | |||
:cssText( Config.stylesTabWrap ) | |||
:node( tbl ) | |||
else | else | ||
r = tbl | |||
end | end | ||
end | end | ||
return r | return r | ||
end -- | end -- features() | ||
Line 689: | Line 750: | ||
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 | ||
r = plaintext._main(r) | |||
if r:find( "&", 1, true ) then | if r:find( "&", 1, true ) then | ||
r = mw.text.decode( r ) | r = mw.text.decode( r ) | ||
Line 786: | Line 837: | ||
if scope:find( "I18N", 1, true ) then | if scope:find( "I18N", 1, true ) then | ||
if s == "string" then | if s == "string" then | ||
elem = | elem = handleNoexportWhitespace( v ) | ||
else | else | ||
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 ] = handleNoexportWhitespace( v ), | |||
[ 2 ] = translated } | |||
else | |||
elem = handleNoexportWhitespace( v ) | |||
end | |||
else | else | ||
elem = v: | elem = false | ||
end | |||
end | |||
if v then | |||
if scope:find( "nowiki", 1, true ) then | |||
elem = mw.text.nowiki( v ) | |||
else | |||
v = flat( v ) | |||
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 | |||
v = mw.text.decode( v ) | |||
elem = v | |||
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 s == "string" then | |||
v = mw.text.nowiki( v ) | |||
elem = v | |||
else | |||
elem = v | |||
end | end | ||
end | end | ||
Line 864: | Line 932: | ||
-- Returns <div> | -- Returns <div> | ||
local r = mw.html.create( "div" ) | local r = mw.html.create( "div" ) | ||
local s = | local s = feasible( Data.tree, true ) | ||
if s then | if s then | ||
r:node( s ) | r:node( s ) | ||
end | |||
if Data.leading then | |||
local toc = mw.html.create( "div" ) | |||
if Config.suppressTOCnum then | |||
toc:addClass( Config.suppressTOCnum ) | |||
end | |||
toc:css( "margin-top", "0.5em" ) | |||
:wikitext( "__TOC__" ) | |||
r:newline() | |||
:node( toc ) | |||
:newline() | |||
end | end | ||
s = features() | s = features() | ||
if s then | if s then | ||
if Data.leading then | |||
r:node( mw.html.create( "h2" ) | |||
:wikitext( getLocalizedText( "doc-params" ) ) ) | |||
:newline() | |||
end | |||
r:node( s ) | r:node( s ) | ||
end | end | ||
Line 899: | Line 983: | ||
local function full() | local function full() | ||
-- Build | -- Build HTML table for display from JSON data, and append an invisible | ||
-- <templatedata> block. | |||
Data.div = mw.html.create( "div" ) | Data.div = mw.html.create( "div" ) | ||
:addClass( "mw-templatedata-doc-wrap" ) | :addClass( "mw-templatedata-doc-wrap" ) | ||
Line 914: | Line 999: | ||
end | end | ||
Data.div:node( format() ) | Data.div:node( format() ) | ||
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( " | if Config.loudly then | ||
-- Display raw templatedata table all the time. | |||
Data.div:node( mw.html.create( "hr" ) ) | |||
Data.div:node( div ) | |||
else | |||
-- Creates an expand link to check raw templatedata table. | |||
local wrapper = mw.html.create( "div" ) | |||
wrapper:addClass( "mw-collapsible" ) | |||
wrapper:addClass( "mw-collapsed" ) | |||
wrapper:css( "font-size", "85%" ) | |||
div:addClass( "mw-collapsible-content" ) | |||
wrapper:wikitext( "'''Test of raw TemplateData output''': " ) | |||
wrapper:node( div ) | |||
Data.div:node( wrapper ) | |||
end | |||
end | end | ||
end | end | ||
end -- full() | end -- full() | ||
Line 932: | Line 1,030: | ||
local function furnish( adapt, arglist ) | local function furnish( adapt, arglist ) | ||
-- | -- Called by f, this function is the first to do any real work when the | ||
-- module is invoked. | |||
-- Parameter: | -- Parameter: | ||
-- adapt -- table, #invoke parameters | -- adapt -- table, #invoke parameters | ||
-- arglist -- table, template parameters | -- arglist -- table, template parameters | ||
-- Returns string | -- Returns string | ||
--local spy="" | |||
local source | local source | ||
for k, v in pairs( Config ) do | for k, v in pairs( Config ) do | ||
Line 944: | Line 1,044: | ||
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.site.server:find( "//de.wikipedia.beta.wmflabs.org", 1, true ) then | --if mw.site.server:find( "//de.wikipedia.beta.wmflabs.org", 1, true ) then | ||
-- Config.loudly = true | |||
end | --end | ||
Data.lazy = faculty( arglist.lazy ) and not Config.loudly | |||
Data.leading = faculty( arglist.TOC ) | |||
if arglist.JSON then | if arglist.JSON then | ||
source = arglist.JSON | source = arglist.JSON | ||
Line 957: | Line 1,059: | ||
source = s | source = s | ||
elseif mw.ustring.sub( s, 1, 8 ) == | elseif mw.ustring.sub( s, 1, 8 ) == | ||
mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then | mw.ustring.char( 127, 39, 34, 96, 85, 78, 73, 81 ) then -- <DEL> ' " ` U N I Q | ||
Data.strip = s | Data.strip = s | ||
end | end | ||
Line 974: | Line 1,076: | ||
end | end | ||
end | end | ||
--if source and | |||
-- ( source:find( "|", 1, true ) or | |||
-- source:find( "}}", 1, true ) ) then | |||
-- -- <ref | |||
--spy=string.format( "[[category:%s]]", Config.strange ) | |||
--end | |||
end | |||
if not Data.lazy and Config.subpage then | |||
if not Data.title then | |||
Data.title = mw.title.getCurrentTitle() | |||
end | |||
Data.lazy = Data.title.text:match( Config.subpage ) | |||
end | end | ||
TemplateData.getPlainJSON( source ) | TemplateData.getPlainJSON( source ) | ||
return finalize() | return finalize() | ||
--return spy .. finalize() | |||
end -- furnish() | end -- furnish() | ||
Line 982: | Line 1,097: | ||
TemplateData.failsafe = function ( assert ) | TemplateData.failsafe = function ( assert ) | ||
-- Checks the age of this implementation against some minimum ("assert"). | |||
local r | local r | ||
if not assert or assert <= TemplateData.serial then | if not assert or assert <= TemplateData.serial then | ||
Line 1,030: | Line 1,146: | ||
p.f = function ( frame ) | p.f = function ( frame ) | ||
-- | -- The entry point for templates invoking the module. | ||
local lucky, | -- Just wraps furnish in an exception handler. | ||
local lucky, result | |||
TemplateData.frame = frame | TemplateData.frame = frame | ||
lucky, | lucky, result = pcall( furnish, frame.args, frame:getParent().args ) | ||
if not lucky then | if not lucky then | ||
Fault( "INTERNAL: " .. | Fault( "INTERNAL: " .. result ) | ||
result = failures() | |||
end | end | ||
return | return result | ||
end -- p.f() | end -- p.f() | ||
Latest revision as of 13:18, 3 September 2020
Documentation for this module may be created at Module:Format TemplateData/doc
local TemplateData = { serial = "2017-11-06", suite = "TemplateData" } local plaintext = require("Module:Plain text") --[=[ improve template:TemplateData ]=] local Config = { -- multiple #invoke option names mapped into unique internal fields cat = "strange", classNoNumTOC = "suppressTOCnum", -- classParams = "classTable", cssParams = "stylesTable", cssParWrap = "stylesTabWrap", debug = false, docpageCreate = "suffix", docpageDetect = "subpage", msgDescMiss = "solo", -- classTable = false, -- class for params table loudly = false, -- show exported element, etc. solo = false, -- complaint on missing description strange = false, -- title of maintenance category stylesTable = false, -- styles for params table stylesTabWrap = false, -- styles for params table wrapper subpage = false, -- pattern to identify subpage suffix = false, -- subpage creation scheme suppressTOCnum = false -- class for TOC number suppression } local Data = { div = false, -- <div class="mw-templatedata-doc-wrap"> 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 slang = false, -- project 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 = { styles = { required = "border-left: 3px solid black;", suggested = "border-left: 3px solid #888;", optional = "border-left: 3px solid #ccc", deprecated = "border-left: 3px dotted red; background-color: #FDD;", tableheadbg = "background-color: #B3B7FF;" }, params = { aliases = "table", autovalue = "string", default = "string table I18N nowiki", deprecated = "boolean string", description = "string table I18N", example = "string table I18N nowiki", label = "string table I18N", inherits = "string", required = "boolean", 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" } } -- -- Generic utility functions -- 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 collapseWhitespace ( a ) -- Collapses whitespace, HTML style. return a:gsub( "%s*\n%s*", " " ) :gsub( "%s%s+", " " ) end -- collapseWhitespace -------------------------------------------------------------------------------- -- 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 getLocalizedText( adapt ) -- Retrieve localized text from system message -- Parameter: -- adapt -- string, message ID after "templatedata-" -- Returns string, with localized text return mw.message.new( "templatedata-" .. adapt ):plain() end -- getLocalizedText() 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'''<br />" .. Data.scream ) if Config.strange then r = string.format( "%s[[category:%s]]", r, Config.strange ) end else r = "" end return r end -- failures() local function handleNoexportWhitespace( adjust ) -- Reduces runs of spaces, including newlines, to a single space, so the -- whole string is on one line. <noexport> blocks are left alone, but the -- <noexport> tags themselves are removed. -- adjust -- string -- Returns string, with adjusted text 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 .. collapseWhitespace( 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 = collapseWhitespace( adjust ) end return r end -- handleNoexportWhitespace() local function faraway( alternatives ) -- Retrieve project 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 if not Data.slang then Data.slang = mw.language.getContentLanguage():getCode() end for k, v in pairs( alternatives ) do if type( v ) == "string" then v = mw.text.trim( v ) if v ~= "" then variants[ k ] = v n = n + 1 end end end -- for k, v if n > 0 then for k, v in pairs( variants ) do if v then if n == 1 then r1 = v elseif k:lower() == Data.slang then variants[ k ] = nil r1 = v r2 = variants break -- for k, v end end end -- for k, v if not r1 then local seek = string.format( "^%s-", Data.slang ) for k, v in pairs( variants ) do if v and k:lower():match( seek ) then variants[ k ] = nil r1 = v r2 = variants break -- for k, v end end -- for k, v if not r1 then local others = mw.language.getFallbacksFor( slang ) table.insert( others, "en" ) for i = 1, #others do seek = others[ i ] if variants[ seek ] then r1 = variants[ seek ] variants[ seek ] = nil r2 = variants break -- for i end end -- i = 1, #others end if not r1 then for k, v in pairs( variants ) do if v then variants[ k ] = nil r1 = v r2 = variants break -- for k, v end end -- for k, v end end if r2 then for k, v in pairs( r2 ) do if v then local baseCode = k:match( "^%s*(%a%a%a?)-?%a*%s*$" ) if not baseCode or not mw.language.isKnownLanguageTag( baseCode ) then Fault( string.format( "Invalid <code>lang=%s</code>", k )) end end end -- for k, v end end return r1, r2 end -- faraway() 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 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 = { } 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 end end -- for k, v 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 feasible( about, asked ) -- Create description head -- Parameter: -- about -- table, supposed to contain description -- asked -- true, if mandatory description -- Returns <block>, with head, or nil local para = 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" ) if not Config.loudly then plus:addClass( "templatedata-maintain" ) :css( "display", "none" ) end for k, v in pairs( about.description[ 2 ] ) do plus:node( mw.html.create( "li" ) :node( mw.html.create( "code" ) :wikitext( k ) ) :node( mw.html.create( "br" ) ) :wikitext( handleNoexportWhitespace( v ) ) ) end -- for k, v end 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 -- feasible() 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 = { } for k, v in pairs( Data.tree.params ) do i = facet( k, 1 ) 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 <tr> 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.<code>%s</code>", 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.<code>%s</code>.aliases", access ) Fault( getLocalizedText( "invalid-value" ):gsub( "$1", s ) ) legal = false end end -- description etc. s = feasible( param ) if s then desc:node( s ) 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 section = getLocalizedText( "doc-param-" .. s ) dt = mw.html.create( "dt" ):wikitext( section ) dd = mw.html.create( "dd" ) if (string.len(show) < 80) then dt:cssText("float: left;margin-right: 1.6em;") end if param.type == "boolean" then if show == "0" then dd:wikitext("<span style=\"color: #610; font-weight: bold;\">False</span> (<code>0</code>)") elseif show == "1" then dd:wikitext("<span style=\"color: #050; font-weight: bold;\">True</span> (<code>1</code>)") else dd:wikitext( show ) end else dd:wikitext( show ) end dl:node( dt ) :node( dd ) end end -- i = 1, #details desc:node( dl ) end -- type 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( getLocalizedText( "doc-param-type-" .. s ) ) :tag( "br" ) typed:node( mw.html.create( "span" ) :addClass( "error" ) :wikitext( param.type ) ) Data.lasting = true else s = getLocalizedText( "doc-param-type-" .. param.type ) typed:wikitext( s ) end else Data.params[ access ].type = "unknown" typed:addClass( "error" ) :wikitext( "INVALID" ) s = string.format( "params.<code>%s</code>.type", access ) Fault( getLocalizedText( "invalid-value" ):gsub( "$1", s ) ) legal = false end else typed = mw.html.create( "td" ) :wikitext( getLocalizedText( "doc-param-type-unknown" ) ) end -- status if param.required then mode = 1 if param.deprecated then Fault( string.format( "Required deprecated <code>%s</code>", access ) ) legal = false end elseif param.deprecated then mode = 4 elseif param.suggested then mode = 2 else mode = 3 end status = ranking[ mode ] ranking = getLocalizedText( "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 -- <tr> r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) ) :cssText( Permit.styles[ 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 <table> for parameters -- Returns <table>, or nil local r if Data.tree and Data.tree.params then local style = Permit.styles.tableheadbg local tbl = mw.html.create( "table" ) :addClass( "wikitable" ) local tr = mw.html.create( "tr" ) feat() if Data.order and #Data.order > 1 then tbl:addClass( "sortable" ) end -- if Config.classTable then -- tbl:addClass( Config.classTable ) -- end if Config.stylesTable then tbl:cssText( Config.stylesTable ) end tr:node( mw.html.create( "th" ) :attr( "colspan", "2" ) :cssText( style ) :wikitext( getLocalizedText( "doc-param-name" ) ) ) :node( mw.html.create( "th" ) :cssText( style ) :wikitext( getLocalizedText( "doc-param-desc" ) ) ) :node( mw.html.create( "th" ) :cssText( style ) :wikitext( getLocalizedText( "doc-param-type" ) ) ) :node( mw.html.create( "th" ) :cssText( style ) :wikitext( getLocalizedText( "doc-param-status" ) ) ) tbl:newline() -- :node( mw.html.create( "thead" ) :node( tr ) -- ) :newline() if Data.order then for i = 1, #Data.order do tbl:node( feature( Data.order[ i ] ) ) end -- for i = 1, #Data.order end if Config.stylesTabWrap then r = mw.html.create( "div" ) :cssText( Config.stylesTabWrap ) :node( tbl ) else r = tbl end end return r end -- features() local function finalize() -- Wrap presentation into frame -- Returns string local r if Data.div then r = tostring( Data.div ) elseif Data.strip then r = Data.strip else r = "" end return r .. failures() 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 r = plaintext._main(r) 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( "<code>params.%s</code>", at ) else r = "''root''" end if a then r = string.format( "%s<code>.%s</code>", 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" 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 = handleNoexportWhitespace( v ) else local translated v, translated = faraway( v ) if v then if translated and k == "description" then elem = { [ 1 ] = handleNoexportWhitespace( v ), [ 2 ] = translated } else elem = handleNoexportWhitespace( v ) end else elem = false end end if v then if scope:find( "nowiki", 1, true ) then elem = mw.text.nowiki( v ) else v = flat( v ) end end else if k == "params" and not access then v = nil elem = nil elseif k == "format" and not access then v = mw.text.decode( v ) elem = v elseif k == "inherits" then elem = v if not Data.heirs then Data.heirs = { } end Data.heirs[ slot ] = 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 not Data.params then Data.params = { } end Data.params[ slot ] = { } tag = Data.params[ slot ] else Data.tag = { } tag = Data.tag end end tag[ k ] = v end else s = string.format( "Type <code>%s</code> 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 <code>object</code> type" ) end end -- focus() local function format() -- Build presented documentation -- Returns <div> local r = mw.html.create( "div" ) local s = feasible( Data.tree, true ) if s then r:node( s ) end if Data.leading then local toc = mw.html.create( "div" ) if Config.suppressTOCnum then toc:addClass( Config.suppressTOCnum ) end toc:css( "margin-top", "0.5em" ) :wikitext( "__TOC__" ) r:newline() :node( toc ) :newline() end s = features() if s then if Data.leading then r:node( mw.html.create( "h2" ) :wikitext( getLocalizedText( "doc-params" ) ) ) :newline() end r:node( s ) end if Data.tree and Data.tree.format then local e, style s = Data.tree.format:lower( Data.tree.format ) if s == "inline" or s == "block" then style = "i" else style = "code" end r:node( mw.html.create( "p" ) :wikitext( "Format: " ) :node( mw.html.create( style ) :wikitext( s ) ) ) end return r end -- format() local function free() -- Remove JSON comment lines Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([},\"'])", "%1%3" ) end -- free() local function full() -- Build HTML table for display from JSON data, and append an invisible -- <templatedata> block. 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( format() ) 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 -- Display raw templatedata table all the time. Data.div:node( mw.html.create( "hr" ) ) Data.div:node( div ) else -- Creates an expand link to check raw templatedata table. local wrapper = mw.html.create( "div" ) wrapper:addClass( "mw-collapsible" ) wrapper:addClass( "mw-collapsed" ) wrapper:css( "font-size", "85%" ) div:addClass( "mw-collapsible-content" ) wrapper:wikitext( "'''Test of raw TemplateData output''': " ) wrapper:node( div ) Data.div:node( wrapper ) end end end end -- full() local function furnish( adapt, arglist ) -- Called by f, this function is the first to do any real work when the -- module is invoked. -- Parameter: -- adapt -- table, #invoke parameters -- arglist -- table, template parameters -- Returns string --local spy="" local source for k, v in pairs( Config ) 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 ) --if mw.site.server:find( "//de.wikipedia.beta.wmflabs.org", 1, true ) then -- Config.loudly = true --end Data.lazy = faculty( arglist.lazy ) and not Config.loudly Data.leading = faculty( arglist.TOC ) if arglist.JSON then source = arglist.JSON 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 -- <DEL> ' " ` U N I Q Data.strip = s end end if not source then Data.title = mw.title.getCurrentTitle() source = find() if not source and Config.subpage and Config.suffix 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 --if source and -- ( source:find( "|", 1, true ) or -- source:find( "}}", 1, true ) ) then -- -- <ref --spy=string.format( "[[category:%s]]", Config.strange ) --end end if not Data.lazy and Config.subpage then if not Data.title then Data.title = mw.title.getCurrentTitle() end Data.lazy = Data.title.text:match( Config.subpage ) end TemplateData.getPlainJSON( source ) return finalize() --return spy .. finalize() end -- furnish() TemplateData.failsafe = function ( assert ) -- Checks the age of this implementation against some minimum ("assert"). local r if not assert or assert <= TemplateData.serial then r = TemplateData.serial else r = false end return r end -- TemplateData.failsafe() 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 Data.source = adapt free() Data.got = mw.text.jsonDecode( Data.source ) if Data.got then full() if Data.lasting then Fault( "deprecated type syntax" ) end if Data.less then Fault( Config.solo ) end elseif not Data.strip then Fault( "fatal JSON error" ) 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 ) -- The entry point for templates invoking the module. -- Just wraps furnish in an exception handler. local lucky, result TemplateData.frame = frame lucky, result = pcall( furnish, frame.args, frame:getParent().args ) if not lucky then Fault( "INTERNAL: " .. result ) result = failures() end return result 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