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>
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 = "-: solid black;",
suggested = "-: solid #888;",
optional = "-: solid #ccc",
deprecated = "-: 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**
" .. Data.scream )
if Config.strange then
r = string.format( "%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 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