Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Format TemplateData: Difference between revisions

From Attu Project
Content deleted Content added
m Fixed error
 
DON'T check for Module:JSONutil. (to stop this template from making ghost transclusion entries on many pages)
 
(5 intermediate revisions by 4 users not shown)
Line 1: Line 1:
local TemplateData = { suite = "TemplateData",
local TemplateData = {
item = 46997995,
serial = "2022-03-10",
serial = "2025-02-07",
item = 46997995 }
suite = "TemplateData"
--[==[
}
improve template:TemplateData
]==]
local Failsafe = TemplateData


local Failsafe = TemplateData


local Config = {
local Config = {
-- multiple option names mapped into unique internal fields
-- Multiple option names mapped into unique internal fields.
basicCnf = { catProblem = "strange",
basicCnf = {
catProblem = "strange",
classMultiColumns = "selMultClm",
classMultiColumns = "selMultClm",
classNoNumTOC = "suppressTOCnum",
classNoNumTOC = "suppressTOCnum",
classTable = "classTable",
classTable = "classTable",
cssParWrap = "cssTabWrap",
cssParWrap = "cssTabWrap",
cssParams = "cssTable",
cssParams = "cssTable",
docpageCreate = "suffix",
docpageDetect = "subpage",
docpageCreate = "suffix",
docpageDetect = "subpage",
helpBoolean = "support4boolean",
helpAliases = "supportAliases",
helpContent = "support4content",
helpBoolean = "support4boolean",
helpDate = "support4date",
helpContent = "support4content",
helpFile = "support4wiki-file-name",
helpDate = "support4date",
helpFormat = "supportFormat",
helpDefault = "support4default",
helpLine = "support4line",
helpFile = "support4wiki-file-name",
helpNumber = "support4number",
helpFormat = "supportFormat",
helpPage = "support4wiki-page-name",
helpLine = "support4line",
helpString = "support4string",
helpNumber = "support4number",
helpTemplate = "support4wiki-template-name",
helpPage = "support4wiki-page-name",
helpURL = "support4url",
helpString = "support4string",
helpUser = "support4wiki-user-name",
helpTemplate = "support4wiki-template-name",
msgDescMiss = "solo",
helpURL = "support4url",
tStylesTOCnum = "stylesTOCnum",
helpUser = "support4wiki-user-name",
tStylesMultiColumns = "stylesMultClm" },
msgDescMiss = "solo",
classTable = { "wikitable" }, -- classes for params table
debugmultilang = "C0C0C0",
tStylesMultiColumns = "stylesMultClm",
tStylesTOCnum = "stylesTOCnum"
loudly = false, -- show exported element, etc.
},
solo = false, -- complaint on missing description
classTable = { "wikitable" }, -- Classes for params table
strange = false, -- title of maintenance category
cssTable = false, -- styles for params table
cssTable = false, -- Styles for params table
cssTabWrap = false, -- styles for params table wrapper
cssTabWrap = false, -- Styles for params table wrapper
debug = false,
debug = false,
debugmultilang = "#c0c0c0",
subpage = false, -- pattern to identify subpage
jsonDebug = "json-code-lint", -- Class for jsonDebug tool
suffix = false, -- subpage creation scheme
suppressTOCnum = false, -- class for TOC number suppression
loudly = false, -- Show exported element, etc.
jsonDebug = "json-code-lint" -- class for jsonDebug tool
solo = false, -- Complaint on missing description
strange = false, -- Title of maintenance category
subpage = false, -- Pattern to identify subpage
suffix = false, -- Subpage creation scheme
suppressTOCnum = false -- Class for TOC number suppression
}
}
local Data = {
local Data = {
div = false, -- <div class="mw-templatedata-doc-wrap">
div = false, -- <div class="mw-templatedata-doc-wrap">
got = false, -- table, initial templatedata object
got = false, -- table, initial templatedata object
heirs = false, -- table, params that are inherited
heirs = false, -- table, params that are inherited
jump = false, -- source position at end of "params"
jump = false, -- source position at end of "params"
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>
lazy = false, -- doc mode; do not generate effective <templatedata>
leading = false, -- show TOC
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
sibling = false, -- TOC juxtaposed
sibling = false, -- TOC juxtaposed
slang = nil, -- project/user language code
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
strip = false, -- <templatedata> evaluation
strip = false, -- <templatedata> evaluation
tag = false, -- table, exported root element
tag = false, -- table, exported root element
title = false, -- page
title = false, -- page
tree = false -- table, rewritten templatedata object
tree = false -- table, rewritten templatedata object
}
}
local Permit = {
local Permit = {
builder = { after = "block",
builder = {
align = "block",
after = "block",
block = "block",
align = "block",
compressed = "block",
block = "block",
dense = "block",
compressed = "block",
grouped = "inline",
dense = "block",
half = "inline",
grouped = "inline",
indent = "block",
half = "inline",
inline = "inline",
indent = "block",
last = "block",
inline = "inline",
lead = "block",
last = "block",
newlines = "*",
lead = "block",
spaced = "inline" },
newlines = "*",
colors = { tableheadbg = "B3B7FF",
spaced = "inline"
},
required = "EAF3FF",
colors = {
suggested = "FFFFFF",
bg = "var(--background-color-base, #fff)",
optional = "EAECF0",
deprecated = "FFCBCB" },
deprecated = "#ffcbcb",
fg = "var(--color-base, #000)",
params = { aliases = "table",
autovalue = "string",
optional = "#eaecf0",
required = "#eaf3ff",
default = "string table I18N nowiki",
suggested = "#fff",
deprecated = "boolean string I18N",
tableheadbg = "var(--background-color-progressive-subtle, #b3b7ff)"
description = "string table I18N",
},
example = "string table I18N nowiki",
params = {
label = "string table I18N",
inherits = "string",
aliases = "table",
required = "boolean",
autovalue = "string",
style = "string table",
default = "string table I18N nowiki",
suggested = "boolean",
deprecated = "boolean string I18N",
suggestedvalues = "string table number boolean",
description = "string table I18N",
example = "string table I18N nowiki",
type = "string" },
root = { description = "string table I18N",
inherits = "string",
format = "string",
label = "string table I18N",
maps = "table",
required = "boolean",
params = "table",
style = "string table",
paramOrder = "table",
suggested = "boolean",
suggestedvalues = "string table number boolean",
sets = "table" },
type = "string"
search = "[{,]%%s*(['\"])%s%%1%%s*:%%s*%%{",
},
types = { boolean = true,
root = {
content = true,
description = "string table I18N",
date = true,
format = "string",
line = true,
maps = "table",
number = true,
paramOrder = "table",
string = true,
params = "table",
unknown = true,
sets = "table"
url = true,
},
["wiki-file-name"] = true,
search = "[{,]%%s*(['\"])%s%%1%%s*:%%s*%%{",
["wiki-page-name"] = true,
types = {
["wiki-template-name"] = true,
["wiki-user-name"] = true,
boolean = true,
["unbalanced-wikitext"] = true,
content = true,
date = true,
["string/line"] = "line",
line = true,
["string/wiki-page-name"] = "wiki-page-name",
number = true,
["string/wiki-user-name"] = "wiki-user-name" }
string = true,
unknown = true,
url = true,
["string/line"] = "line",
["string/wiki-page-name"] = "wiki-page-name",
["string/wiki-user-name"] = "wiki-user-name",
["unbalanced-wikitext"] = true,
["wiki-file-name"] = true,
["wiki-page-name"] = true,
["wiki-template-name"] = true,
["wiki-user-name"] = true
}
}
}


local function Fault(alert)

-- Memorize error message.

-- Parameter:
local function Fault( alert )
-- Memorize error message
-- alert -- string, error message
if Data.scream then
-- Parameter:
Data.scream = string.format("%s *** %s", Data.scream, alert)
-- alert -- string, error message
else
if Data.scream then
Data.scream = string.format( "%s *** %s", Data.scream, alert )
Data.scream = alert
else
end
Data.scream = alert
end
end -- Fault()
end -- Fault()


local function Fetch(ask, allow)

-- Fetch module.

-- Parameter:
local function Fetch( ask, allow )
-- Fetch module
-- ask -- string, with name
-- "/global"
-- Parameter:
-- ask -- string, with name
-- "JSONutil"
-- "/global"
-- "Multilingual"
-- "Multilingual"
-- "Text"
-- "Text"
-- "WLink"
-- "WLink"
-- allow -- true: no error if unavailable
-- Returns table of module
-- allow -- true: no error if unavailable
-- Returns table of module
-- Error: Module not available
local sign = ask
-- error: Module not available
local sign = ask
local r, stem
if sign:sub(1, 1) == "/" then
local r, stem
sign = TemplateData.frame:getTitle() .. sign
if sign:sub( 1, 1 ) == "/" then
else
sign = TemplateData.frame:getTitle() .. sign
else
stem = sign
stem = sign
sign = "Module:" .. stem
end
sign = "Module:" .. stem
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), 0)
end
end
end
if TemplateData.extern then
return r
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 ), 0 )
end
end
return r
end -- Fetch()
end -- Fetch()




local function Foreign()
local function Foreign()
-- Guess human language
-- Guess human language, returns slang or not.
if type(Data.slang) == "nil" then
-- Returns slang, or not
local Multilingual = Fetch("Multilingual", true)
if type( Data.slang ) == "nil" then
local Multilingual = Fetch( "Multilingual", true )
if Multilingual and type(Multilingual.userLangCode) == "function" then
if Multilingual and
Data.slang = Multilingual.userLangCode()
else
type( Multilingual.userLangCode ) == "function" then
Data.slang = Multilingual.userLangCode()
Data.slang = mw.language.getContentLanguage():getCode():lower()
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
end
end
return Data.slang
if Data.slang and mw.ustring.codepoint(Data.slang, 1, 1) > 122 then
Data.slang = false
end
return Data.slang
end -- Foreign()
end -- Foreign()


local function facet(ask, at)

-- Find physical position of parameter definition in JSON.

-- Parameter:
local function facet( ask, at )
-- ask -- string, parameter name
-- Find physical position of parameter definition in JSON
-- at -- number, physical position within definition
-- Parameter:
-- Returns number or nil.
-- ask -- string, parameter name
local seek = string.format(Permit.search, ask:gsub("%%", "%%%%"):gsub("([%-.()+*?^$%[%]])", "%%%1"))
-- at -- number, physical position within definition
local i, k, r, slice, source
-- Returns number, or nil
if not Data.jump then
local seek = string.format( Permit.search,
Data.jump = Data.source:find("params", 2)
ask:gsub( "%%", "%%%%" )
if Data.jump then
:gsub( "([%-.()+*?^$%[%]])",
Data.jump = Data.jump + 7
"%%%1" ) )
else
local i, k, r, slice, source
if not Data.jump then
Data.jump = 1
Data.jump = Data.source:find( "params", 2 )
if Data.jump then
Data.jump = Data.jump + 7
else
Data.jump = 1
end
end
end
end
i, k = Data.source:find( seek, at + Data.jump )
i, k = Data.source:find(seek, at + Data.jump)
while i and not r do
while i and not r do
source = Data.source:sub( k + 1 )
slice = source:match( "^%s*\"([^\"]+)\"s*:" )
source = Data.source:sub(k + 1)
slice = source:match('^%s*"([^"]+)"s*:')
if not slice then
slice = source:match( "^%s*'([^']+)'%s*:" )
if not slice then
slice = source:match("^%s*'([^']+)'%s*:")
end
end
if ( slice and Permit.params[ slice ] ) or
source:match( "^%s*%}" ) then
if (slice and Permit.params[slice]) or source:match("^%s*%}") then
r = k
r = k
else
else
i, k = Data.source:find( seek, k )
i, k = Data.source:find(seek, k)
end
end
end -- while i
end -- while i
return r
return r
end -- facet()
end -- facet()


local function facilities(apply)

-- Retrieve details of suggestedvalues.

-- Parameter:
local function facilities( apply )
-- apply -- table, with plain or enhanced values
-- Retrieve details of suggestedvalues
-- .suggestedvalues -- table|string|number, or more
-- Parameter:
-- Returns
-- apply -- table, with plain or enhanced values
-- .suggestedvalues -- table|string|number, or more
-- 1 -- table, with suggestedvalues
-- 2 -- table, with CSS map, or not
-- Returns
-- 1 -- table, with suggestedvalues
-- 3 -- string, with class, or not
-- 2 -- table, with CSS map, or not
-- 4 -- string, with templatestyles, or not
local elements = apply.suggestedvalues
-- 3 -- string, with class, or not
local s = type(elements)
-- 4 -- string, with templatestyles, or not
local r1, r2, r3, r4
local elements = apply.suggestedvalues
if s == "table" then
local s = type( elements )
local r1, r2, r3, r4
local values = elements.values
if s == "table" then
if type(values) == "table" then
local values = elements.values
r1 = values
if type( values ) == "table" then
if type(elements.scroll) == "string" then
r1 = values
r2 = r2 or {}
if type( elements.scroll ) == "string" then
r2.height = apply.scroll
r2 = r2 or { }
r2.overflow = "auto"
end
r2.height = apply.scroll
r2.overflow = "auto"
if type(elements.minwidth) == "string" then
end
local s = type(elements.maxcolumns)
r2 = r2 or {}
if type( elements.minwidth ) == "string" then
local s = type( elements.maxcolumns )
r2["column-width"] = elements.minwidth
r2 = r2 or { }
if s == "string" or s == "number" then
r2["column-width"] = elements.minwidth
s = tostring(elements.maxcolumns)
if s == "string" or
r2["column-count"] = s
s == "number" then
s = tostring( elements.maxcolumns )
r2["column-count"] = s
end
if type( Config.selMultClm ) == "string" then
r3 = Config.selMultClm
end
if type( Config.stylesMultClm ) == "string" then
local src = Config.stylesMultClm .. "/styles.css"
r4 = TemplateData.frame
:extensionTag( "templatestyles",
nil,
{ src = src } )
end
end
elseif elements and elements ~= "" then
r1 = elements
end
end
elseif s == "string" then
if type(Config.selMultClm) == "string" then
s = mw.text.trim( about )
r3 = Config.selMultClm
if s ~= "" then
r1 = { }
table.insert( r1,
{ code = s } )
end
end
elseif s == "number" then
if type(Config.stylesMultClm) == "string" then
r1 = { }
local src = Config.stylesMultClm .. "/styles.css"
r4 = TemplateData.frame:extensionTag("templatestyles", nil, { src = src })
table.insert( r1,
end
{ code = tostring( elements ) } )
end
elseif elements and elements ~= "" then
r1 = elements
end
elseif s == "string" then
s = mw.text.trim(about)
if s ~= "" then
r1 = {}
table.insert(r1, { code = s })
end
end
elseif s == "number" then
return r1, r2, r3, r4
r1 = {}
table.insert(r1, { code = tostring(elements) })
end
return r1, r2, r3, r4
end -- facilities()
end -- facilities()


local function factory(adapt)

-- Retrieve localized text from system message.

-- Parameter:
local function factory( adapt )
-- adapt -- string, message ID after "templatedata-"
-- Retrieve localized text from system message
-- Returns string, with localized text
-- Parameter:
-- adapt -- string, message ID after "templatedata-"
local o = mw.message.new("templatedata-" .. adapt)
if Foreign() then
-- Returns string, with localized text
o:inLanguage(Data.slang)
local o = mw.message.new( "templatedata-" .. adapt )
end
if Foreign() then
return o:plain()
o:inLanguage( Data.slang )
end
return o:plain()
end -- factory()
end -- factory()


local function faculty(adjust)

-- Test template argument for Boolean.

local function faculty( adjust )
-- adjust -- string or nil
-- Test template arg for boolean
-- Returns boolean.
-- adjust -- string or nil
local s = type(adjust)
local r
-- Returns boolean
local s = type( adjust )
if s == "string" then
r = mw.text.trim(adjust)
local r
if s == "string" then
r = (r ~= "" and r ~= "0")
elseif s == "boolean" then
r = mw.text.trim( adjust )
r = ( r ~= "" and r ~= "0" )
r = adjust
else
elseif s == "boolean" then
r = adjust
r = false
else
end
r = false
return r
end
return r
end -- faculty()
end -- faculty()




local function failures()
local function failures()
-- Retrieve error collection and category
-- Retrieve error collection and category, returns string.
local r
-- Returns string
if Data.scream then
local r
local e = mw.html.create("span"):addClass("error"):wikitext(Data.scream)
if Data.scream then
local e = mw.html.create( "span" )
r = tostring(e)
mw.addWarning("'''TemplateData'''<br />" .. Data.scream)
:addClass( "error" )
if Config.strange then
:wikitext( Data.scream )
r = tostring( e )
r = string.format("%s[[category:%s]]", r, Config.strange)
mw.addWarning( "'''TemplateData'''<br />" .. Data.scream )
if Config.strange then
r = string.format( "%s[[category:%s]]",
r,
Config.strange )
end
else
r = ""
end
end
else
return r
r = ""
end
return r
end -- failures()
end -- failures()


local function fair(adjust)

-- Reduce text to one line of plain text, or noexport wikitext blocks.

local function fair( adjust )
-- adjust -- string
-- Returns string with adjusted text.
-- Reduce text to one line of plain text, or noexport wikitext blocks
local f = function(a)
-- adjust -- string
return a:gsub("%s*\n%s*", " "):gsub("%s%s+", " ")
-- Returns string, with adjusted text
end
local f = function ( a )
local tags = {
return a:gsub( "%s*\n%s*", " " )
{
:gsub( "%s%s+", " " )
end
start = "<noexport>",
local tags = { { start = "<noexport>",
stop = "</noexport>",
},
stop = "</noexport>" },
{
{ start = "<exportonly>",
stop = "</exportonly>",
start = "<exportonly>",
stop = "</exportonly>",
l = false }
}
l = false,
},
local r = adjust
}
local i, j, k, s, tag
for m = 1, 2 do
local r = adjust
tag = tags[ m ]
local i, j, k, s, tag
for m = 1, 2 do
if r:find( tag.start, 1, true ) then
s = r
tag = tags[m]
if r:find(tag.start, 1, true) then
r = ""
i = 1
s = r
tag.l = true
r = ""
i = 1
j, k = s:find( tag.start, i, true )
while j do
tag.l = true
if j > 1 then
j, k = s:find(tag.start, i, true)
while j do
r = r .. f( s:sub( i, j - 1 ) )
end
if j > 1 then
i = k + 1
r = r .. f(s:sub(i, j - 1))
end
j, k = s:find( tag.stop, i, true )
if j then
i = k + 1
if m == 1 then
j, k = s:find(tag.stop, i, true)
r = r .. s:sub( i, j - 1 )
if j then
end
if m == 1 then
i = k + 1
r = r .. s:sub(i, j - 1)
end
j, k = s:find( tag.start, i, true )
else
i = k + 1
Fault( "missing " .. tag.stop )
j, k = s:find(tag.start, i, true)
end
else
end -- while j
Fault("missing " .. tag.stop)
r = r .. s:sub( i )
elseif m == 1 then
r = f( r )
end
end
end -- for m
end -- while j
if tags[ 2 ].l then
r = r .. s:sub(i)
elseif m == 1 then
r = r:gsub( "<exportonly>.*</exportonly>", "" )
r = f(r)
end
end
return r
end -- for m
if tags[2].l then
r = r:gsub("<exportonly>.*</exportonly>", "")
end
return r
end -- fair()
end -- fair()


local function fancy(advance, alert)

-- Present JSON source.

-- Parameter:
local function fancy( advance, alert )
-- Present JSON source
-- advance -- true, for nice
-- alert -- true, for visible
-- Parameter:
-- Returns string.
-- advance -- true, for nice
local r
-- alert -- true, for visible
if Data.source then
-- Returns string
local r
local support = Config.jsonDebug
local css
if Data.source then
if advance then
local support = Config.jsonDebug
local css
css = {
if advance then
height = "6em",
css = { height = "6em",
resize = "vertical",
}
resize = "vertical" }
r = { [ 1 ] = "syntaxhighlight",
r = {
[ 2 ] = Data.source,
[1] = "syntaxhighlight",
lang = "json",
[2] = Data.source,
style = table.concat( css, ";" ) }
lang = "json",
if alert then
style = table.concat(css, ";"),
}
r.class( support )
end
if alert then
r.class(support)
r = TemplateData.frame:callParserFunction( "#tag", r )
else
end
r = TemplateData.frame:callParserFunction("#tag", r)
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
else
r = ""
css = {
["font-size"] = "0.77em",
["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
end
return r
r = "\n" .. r
else
r = ""
end
return r
end -- fancy()
end -- fancy()


local function faraway(alternatives)

-- Retrieve best language version from multilingual text.

-- Parameter:
local function faraway( alternatives )
-- alternatives -- table, to be evaluated
-- Retrieve best language version from multilingual text
-- Parameter:
-- Returns
-- alternatives -- table, to be evaluated
-- 1 -- string, with best match
-- 2 -- table of other versions, if any
-- Returns
local n = 0
-- 1 -- string, with best match
local variants = {}
-- 2 -- table of other versions, if any
local n = 0
local r1, r2
for k, v in pairs(alternatives) do
local variants = { }
if type(v) == "string" then
local r1, r2
for k, v in pairs( alternatives ) do
v = mw.text.trim(v)
if type( v ) == "string" then
if v ~= "" and type(k) == "string" then
v = mw.text.trim( v )
k = k:lower()
if v ~= "" and type( k ) == "string" then
variants[k] = v
k = k:lower()
n = n + 1
end
variants[ k ] = v
end
n = n + 1
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
end -- for k, v
end -- for k, v
if n > 0 then
end
local Multilingual = Fetch( "Multilingual", true )
if r2 and Multilingual then
if Multilingual and
for k, v in pairs(r2) do
type( Multilingual.i18n ) == "function" then
if v and not Multilingual.isLang(k, true) then
Fault(string.format("%s <code>lang=%s</code>", "Invalid", k))
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 <code>lang=%s</code>",
"Invalid",
k ) )
end
end -- for k, v
end
end
end -- for k, v
end
end
end
return r1, r2
return r1, r2
end -- faraway()
end -- faraway()


local function fashioned(about, asked, assign)

-- Create description head.

-- Parameter:
local function fashioned( about, asked, assign )
-- about -- table, supposed to contain description
-- Create description head
-- asked -- true, if mandatory description
-- Parameter:
-- about -- table, supposed to contain description
-- assign -- <block>, if to be equipped
-- Returns <block> with head, or nil.
-- asked -- true, if mandatory description
-- assign -- <block>, if to be equipped
local para = assign or mw.html.create("div")
local plus, r
-- Returns <block>, with head, or nil
if about and about.description then
local para = assign or mw.html.create( "div" )
if type(about.description) == "string" then
local plus, r
if about and about.description then
para:wikitext(about.description)
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
else
para = false
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)
:css("color", "inherit")
:node(plus)
else
plus:addClass("templatedata-maintain"):css("display", "none")
end
end
end
if para then
elseif Config.solo and asked then
para:addClass("error"):wikitext(Config.solo)
if plus then
Data.less = true
r = mw.html.create( "div" )
else
:node( para )
para = false
:node( plus )
end
else
if para then
r = para
end
if plus then
r = mw.html.create("div"):node(para):node(plus)
else
r = para
end
end
end
return r
return r
end -- fashioned()
end -- fashioned()


local function fatten(access)

-- Create table row for subheadline.

-- Parameter:
local function fatten( access )
-- access -- string, with name
-- Create table row for sub-headline
-- Parameter:
-- Returns <tr>.
local param = Data.tree.params[access]
-- access -- string, with name
local sub, sort = access:match("(=+)%s*(%S.*)$")
-- Returns <tr>
local param = Data.tree.params[ access ]
local headline = mw.html.create(string.format("h%d", #sub))
local sub, sort = access:match( "(=+)%s*(%S.*)$" )
local r = mw.html.create("tr")
local headline = mw.html.create( string.format( "h%d", #sub ) )
local td = mw.html.create("td"):attr("colspan", "5"):attr("data-sort-value", "!" .. sort)
local s
local r = mw.html.create( "tr" )
if param.style then
local td = mw.html.create( "td" )
s = type(param.style)
:attr( "colspan", "5" )
if s == "table" then
:attr( "data-sort-value", "!" .. sort )
td:css(param.style)
local s
if param.style then
elseif s == "string" then
s = type( param.style )
td:cssText(param.style)
if s == "table" then
td:css( param.style )
elseif s == "string" then
td:cssText( param.style )
end
end
end
end
s = fashioned( param, false, headline )
s = fashioned(param, false, headline)
if s then
if s then
headline = s
else
headline = s
else
headline:wikitext( sort )
headline:wikitext(sort)
end
end
td:node( headline )
r:node( td )
td:node(headline)
r:node(td)
return r
return r
end -- fatten()
end -- fatten()




local function fathers()
local function fathers()
-- Merge params with inherited values
-- Merge parameters with inherited values.
local n = 0
local n = 0
local p = Data.params
local p = Data.params
local t = Data.tree.params
local t = Data.tree.params
local p2, t2
local p2, t2
for k, v in pairs( Data.heirs ) do
for k, v in pairs(Data.heirs) do
n = n + 1
n = n + 1
end -- for k, v
end -- for k, v
for i = 1, n do
for i = 1, n do
if Data.heirs then
if Data.heirs then
for k, v in pairs( Data.heirs ) do
for k, v in pairs(Data.heirs) do
if v and not Data.heirs[ v ] then
if v and not Data.heirs[v] then
n = n - 1
n = n - 1
t[ k ].inherits = nil
t[k].inherits = nil
Data.heirs[ k ] = nil
Data.heirs[k] = nil
p2 = { }
p2 = {}
t2 = { }
t2 = {}
if p[ v ] then
if p[v] then
for k2, v2 in pairs( p[ v ] ) do
for k2, v2 in pairs(p[v]) do
p2[ k2 ] = v2
p2[k2] = v2
end -- for k2, v2
end -- for k2, v2
if p[ k ] then
if p[k] then
for k2, v2 in pairs( p[ k ] ) do
for k2, v2 in pairs(p[k]) do
if type( v2 ) ~= "nil" then
if type(v2) ~= "nil" then
p2[ k2 ] = v2
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 -- for k, v
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 -- i = 1, n
end -- for k, v
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
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 &#124; %s", s, k)
else
s = "Circular inherits: " .. k
end
end
end -- for k, v
Fault(s)
end
end -- fathers()
end -- fathers()




local function favorize()
local function favorize()
-- Local customization issues
-- Local customization issues.
local boole = { ["font-size"] = "125%" }
local boole = { ["font-size"] = "1.25em" }
local l, cx = pcall( mw.loadData,
local l, cx = pcall(mw.loadData, TemplateData.frame:getTitle() .. "/config")
local scripting, style
TemplateData.frame:getTitle() .. "/config" )
TemplateData.ltr = not mw.language.getContentLanguage():isRTL()
local scripting, style
TemplateData.ltr = not mw.language.getContentLanguage():isRTL()
if TemplateData.ltr then
scripting = "left"
if TemplateData.ltr then
else
scripting = "left"
scripting = "right"
end
boole["margin-" .. scripting] = "3em"
Permit.boole = {
[false] = {
css = boole,
lead = true,
show = "&#x2610;",
},
[true] = {
css = boole,
lead = true,
show = "&#x2611;",
},
}
Permit.css = {}
for k, v in pairs(Permit.colors) do
if k == "tableheadbg" then
k = "tablehead"
end
if k == "fg" then
style = "color"
else
else
scripting = "right"
style = "background-color"
end
end
Permit.css[k] = {}
boole[ "margin-" .. scripting ] = "3em"
Permit.boole = { [false] = { css = boole,
Permit.css[k][style] = v
end -- for k, v
lead = true,
if type(cx) == "table" then
show = "☐" },
local c, s
[true] = { css = boole,
if type(cx.permit) == "table" then
lead = true,
show = "" } }
if type(cx.permit.boole) == "table" then
if type(cx.permit.boole[true]) == "table" then
Permit.css = { }
for k, v in pairs( Permit.colors ) do
Permit.boole[false] = cx.permit.boole[false]
if k == "tableheadbg" then
k = "tablehead"
end
end
if k == "fg" then
if type(cx.permit.boole[true]) == "table" then
style = "color"
Permit.boole[true] = cx.permit.boole[true]
else
style = "background-color"
end
end
Permit.css[ k ] = { }
end
Permit.css[ k ][ style ] = "#" .. v
if type(cx.permit.css) == "table" then
end -- for k, v
for k, v in pairs(cx.permit.css) do
if type( cx ) == "table" then
if type(v) == "table" then
local c, s
Permit.css[k] = v
if type( cx.permit ) == "table" then
end
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 -- for k, v
end
end
end
if type( Config.subpage ) ~= "string" or
for k, v in pairs(Config.basicCnf) do
type( Config.suffix ) ~= "string" then
s = type(cx[k])
if s == "string" or s == "table" then
local got = mw.message.new( "templatedata-doc-subpage" )
local suffix
Config[v] = cx[k]
end
if got:isDisabled() then
suffix = "doc"
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 feasible(all, at, about)
-- Deal with suggestedvalues within parameter.
-- Parameter:
-- all -- parameter details
-- .default
-- .type
-- at -- string, with parameter name
-- about -- suggestedvalues -- table,
-- value and possibly description
-- table may have elements:
-- .code -- mandatory
-- .label -- table|string
-- .support -- table|string
-- .icon -- string
-- .class -- table|string
-- .css -- table
-- .style -- string
-- .less -- true: suppress code
-- Returns
-- 1: mw.html object <ul>
-- 2: sequence table with values, or nil
local h = {}
local e, r1, r2, s, v
if #about > 0 then
for i = 1, #about do
e = about[i]
s = type(e)
if s == "table" then
if type(e.code) == "string" then
s = mw.text.trim(e.code)
if s == "" then
e = nil
else
e.code = s
end
else
else
suffix = got:plain()
e = nil
s = string.format("params.%s.%s[%d] %s", at, "suggestedvalues", i, "MISSING 'code:'")
end
end
if type( Config.subpage ) ~= "string" then
elseif s == "string" then
Config.subpage = string.format( "/%s$", suffix )
s = mw.text.trim(e)
if s == "" then
e = nil
s = string.format("params.%s.%s[%d] EMPTY", at, "suggestedvalues", i)
Fault(s)
else
e = { code = s }
end
end
if type( Config.suffix ) ~= "string" then
elseif s == "number" then
Config.suffix = string.format( "%%s/%s", suffix )
e = { code = tostring(e) }
else
s = string.format("params.%s.%s[%d] INVALID", at, "suggestedvalues", i)
Fault(s)
e = false
end
if e then
v = v or {}
table.insert(v, e)
if h[e.code] then
s = string.format("params.%s.%s REPEATED %s", at, "suggestedvalues", e.code)
Fault(s)
else
h[e.code] = true
end
end
end
end
end -- favorize()
end -- for i
else

Fault(string.format("params.%s.suggestedvalues %s", at, "NOT AN ARRAY"))

end

if v then
local function feasible( all, at, about )
local code, d, k, less, story, swift, t, u
-- Deal with suggestedvalues within parameter
r1 = mw.html.create("ul")
-- Parameter:
r2 = {}
-- all -- parameter details
-- .default
for i = 1, #v do
-- .type
u = mw.html.create("li")
e = v[i]
-- at -- string, with parameter name
table.insert(r2, e.code)
-- about -- suggestedvalues -- table,
story = false
-- value and possibly description
less = (e.less == true)
-- table may have elements:
if not less then
-- .code -- mandatory
swift = e.code
-- .label -- table|string
-- .support -- table|string
if e.support then
local scream, support
-- .icon -- string
s = type(e.support)
-- .class -- table|string
if s == "string" then
-- .css -- table
-- .style -- string
support = e.support
elseif s == "table" then
-- .less -- true: suppress code
support = faraway(e.support)
-- Returns
-- 1: mw.html object <ul>
else
-- 2: sequence table with values, or nil
scream = "INVALID"
local h = { }
end
local e, r1, r2, s, v
if support then
s = mw.text.trim(support)
if #about > 0 then
for i = 1, #about do
if s == "" then
e = about[ i ]
scream = "EMPTY"
s = type( e )
elseif s:find("[%[%]|%<%>]") then
if s == "table" then
scream = "BAD PAGE"
if type( e.code ) == "string" then
s = mw.text.trim( e.code )
if s == "" then
e = nil
else
e.code = s
end
else
e = nil
s = string.format( "params.%s.%s[%d] %s",
at,
"suggestedvalues",
i,
"MISSING 'code:'" )
end
elseif s == "string" then
s = mw.text.trim( e )
if s == "" then
e = nil
s = string.format( "params.%s.%s[%d] EMPTY",
at, "suggestedvalues", i )
Fault( s )
else
e = { code = s }
end
elseif s == "number" then
e = { code = tostring( e ) }
else
else
s = string.format( "params.%s.%s[%d] INVALID",
support = s
at, "suggestedvalues", i )
Fault( s )
e = false
end
end
if e then
end
v = v or { }
if scream then
table.insert( v, e )
s = string.format("params.%s.%s[%d].support %s", at, "suggestedvalues", i, scream)
if h[ e.code ] then
Fault(s)
else
s = string.format( "params.%s.%s REPEATED %s",
swift = string.format("[[:%s|%s]]", support, swift)
at,
end
"suggestedvalues",
end
e.code )
Fault( s )
if all.type:sub(1, 5) == "wiki-" and swift == e.code then
else
local rooms = {
h[ e.code ] = true
file = 6,
end
temp = 10,
end
user = 2,
end -- for i
}
local ns = rooms[all.type:sub(6, 9)] or 0
else
t = mw.title.makeTitle(ns, swift)
Fault( string.format( "params.%s.suggestedvalues %s",
at, "NOT AN ARRAY" ) )
if t and t.exists then
swift = string.format("[[:%s|%s]]", t.prefixedText, swift)
end
if v then
end
end
local code, d, k, less, story, swift, t, u
r1 = mw.html.create( "ul" )
if e.code == all.default then
r2 = { }
k = 800
for i = 1, #v do
else
u = mw.html.create( "li" )
k = 300
e = v[ i ]
end
table.insert( r2, e.code )
code =
mw.html.create("code"):css("font-weight", tostring(k)):css("white-space", "nowrap"):wikitext(swift)
story = false
less = ( e.less == true )
u:node(code)
if not less then
end
swift = e.code
if e.class then
if e.support then
s = type(e.class)
local scream, support
if s == "string" then
s = type( e.support )
u:addClass(e.class)
if s == "string" then
elseif s == "table" then
support = e.support
for k, s in pairs(e.class) do
elseif s == "table" then
u:addClass(s)
support = faraway( e.support )
end -- for k, s
else
else
s = string.format("params.%s.%s[%d].class INVALID", at, "suggestedvalues", i)
scream = "INVALID"
end
Fault(s)
if support then
end
end
s = mw.text.trim( support )
if s == "" then
if e.css then
scream = "EMPTY"
if type(e.css) == "table" then
elseif s:find( "[%[%]|%<%>]" ) then
u:css(e.css)
else
scream = "BAD PAGE"
s = string.format("params.%s.%s[%d].css INVALID", at, "suggestedvalues", i)
else
support = s
Fault(s)
end
end
end
end
if scream then
if e.style then
s = string.format( "params.%s.%s[%d].support %s",
if type(e.style) == "string" then
at,
u:cssText(e.style)
else
"suggestedvalues",
s = string.format("params.%s.%s[%d].style INVALID", at, "suggestedvalues", i)
i,
scream )
Fault(s)
Fault( s )
end
else
end
if all.type == "wiki-file-name" and not e.icon then
swift = string.format( "[[:%s|%s]]",
e.icon = e.code
support, swift )
end
end
end
if e.label then
if all.type:sub( 1, 5 ) == "wiki-" and
s = type(e.label)
swift == e.code then
if s == "string" then
local rooms = { file = 6,
s = mw.text.trim(e.label)
temp = 10,
if s == "" then
s = string.format("params.%s.%s[%d].label %s", at, "suggestedvalues", i, "EMPTY")
user = 2 }
local ns = rooms[ all.type:sub( 6, 9 ) ] or 0
Fault(s)
else
t = mw.title.makeTitle( ns, swift )
if t and t.exists then
story = s
end
swift = string.format( "[[:%s|%s]]",
elseif s == "table" then
t.prefixedText, swift )
end
story = faraway(e.label)
end
else
s = string.format("params.%s.%s[%d].label INVALID", at, "suggestedvalues", i)
if e.code == all.default then
k = 800
Fault(s)
else
end
k = 300
end
end
s = false
code = mw.html.create( "code" )
if type(e.icon) == "string" then
:css( "font-weight", tostring( k ) )
t = mw.title.makeTitle(6, e.icon)
if t and t.file.exists then
:css( "white-space", "nowrap" )
:wikitext( swift )
local g = mw.html.create("span")
u:node( code )
s = string.format("[[%s|16px]]", t.prefixedText)
end
g:attr("role", "presentation"):wikitext(s)
if e.class then
s = tostring(g)
s = type( e.class )
end
end
if s == "string" then
u:addClass( e.class )
if not s and not less and e.label then
elseif s == "table" then
s = mw.ustring.char(0x2013)
end
for k, s in pairs( e.class ) do
u:addClass( s )
if s then
end -- for k, s
d = mw.html.create("span"):wikitext(s)
else
if TemplateData.ltr then
if not less then
s = string.format( "params.%s.%s[%d].class INVALID",
at, "suggestedvalues", i )
d:css("margin-left", "0.5em")
Fault( s )
end
end
if story then
end
d:css("margin-right", "0.5em")
if e.css then
end
else
if type( e.css ) == "table" then
u:css( e.css )
if not less then
else
d:css("margin-right", "0.5em")
end
s = string.format( "params.%s.%s[%d].css INVALID",
if story then
at, "suggestedvalues", i )
Fault( s )
d:css("margin-left", "0.5em")
end
end
end
end
if e.style then
u:node(d)
end
if type( e.style ) == "string" then
if story then
u:cssText( e.style )
else
u:wikitext(story)
end
s = string.format( "params.%s.%s[%d].style INVALID",
r1:newline():node(u)
at, "suggestedvalues", i )
end -- for i
Fault( s )
end
end
end
if not r1 and v ~= false then
Fault(string.format("params.%s.suggestedvalues INVALID", at))
if all.type == "wiki-file-name" and not e.icon then
r1 = mw.html.create("code"):addClass("error"):wikitext("INVALID")
e.icon = e.code
end
end
return r1, r2
if e.label then
s = type( e.label )
if s == "string" then
s = mw.text.trim( e.label )
if s == "" then
s = string.format( "params.%s.%s[%d].label %s",
at,
"suggestedvalues",
i,
"EMPTY" )
Fault( s )
else
story = s
end
elseif s == "table" then
story = faraway( e.label )
else
s = string.format( "params.%s.%s[%d].label INVALID",
at, "suggestedvalues", i )
Fault( s )
end
end
s = false
if type( e.icon ) == "string" then
t = mw.title.makeTitle( 6, e.icon )
if t and t.file.exists then
local g = mw.html.create( "span" )
s = string.format( "[[%s|16px]]", t.prefixedText )
g:attr( "role", "presentation" )
:wikitext( s )
s = tostring( g )
end
end
if not s and not less and e.label then
s = mw.ustring.char( 0x2013 )
end
if s then
d = mw.html.create( "span" )
:wikitext( s )
if TemplateData.ltr then
if not less then
d:css( "margin-left", "0.5em" )
end
if story then
d:css( "margin-right", "0.5em" )
end
else
if not less then
d:css( "margin-right", "0.5em" )
end
if story then
d:css( "margin-left", "0.5em" )
end
end
u:node( d )
end
if story then
u:wikitext( story )
end
r1:newline()
:node( u )
end -- for i
end
if not r1 and v ~= false then
Fault( string.format( "params.%s.suggestedvalues INVALID", at ) )
r1 = mw.html.create( "code" )
:addClass( "error" )
:wikitext( "INVALID" )
end
return r1, r2
end -- feasible()
end -- feasible()




local function feat()
local function feat()
-- Check and store parameter sequence
-- Check and store parameter sequence.
if Data.source then
if Data.source then
local i = 0
local i = 0
local s
local s
for k, v in pairs( Data.tree.params ) do
for k, v in pairs(Data.tree.params) do
if i == 0 then
if i == 0 then
Data.order = { }
Data.order = {}
i = 1
i = 1
s = k
s = k
else
else
i = 2
i = 2
break -- for k, v
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
end
end -- for k, v
else
if i > 1 then
given[s] = 1
local pointers = { }
end
local points = { }
end
local given = { }
if i then
for k, v in pairs( Data.tree.params ) do
table.insert(points, i)
i = facet( k, 1 )
pointers[i] = k
if type( v ) == "table" then
i = facet(k, i)
if type( v.label ) == "string" then
if i then
s = mw.text.trim( v.label )
s = "Parameter '%s' detected twice"
if s == "" then
Fault(string.format(s, k))
s = k
end
end
else
else
s = "Parameter '%s' not detected"
s = k
Fault(string.format(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 -- 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
end -- feat()
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 eager = 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 styles = "mw-templatedata-doc-param-"
local sort, typed


for k, v in pairs(param) do
if v == "" then
param[k] = false
end
end -- for k, v


-- label
local function feature( access )
sort = param.label or access
-- Create table row for parameter, check and display violations
if sort:match("^%d+$") then
-- Parameter:
begin:attr("data-sort-value", string.format("%05d", tonumber(sort)))
-- access -- string, with name
end
-- Returns <tr>
begin:css("font-weight", "700"):wikitext(sort)
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 eager = 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 styles = "mw-templatedata-doc-param-"
local sort, typed


-- name and aliases
for k, v in pairs( param ) do
code:css("font-size", "0.92em"):css("white-space", "nowrap"):wikitext(access)
if v == "" then
if not fine(access) then
param[ k ] = false
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"):addClass(styles .. "name"):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, syn
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"))
:wikitext(s)
else
if Config.supportAliases then
s = string.format("[[%s|%s]]", Config.supportAliases, mw.text.nowiki(s))
end
syn = mw.html.create("span"):addClass(styles .. "alias"):css("white-space", "nowrap"):wikitext(s)
code:node(syn)
end
end
else
lapsus = true
code:node(mw.html.create("code"):addClass("error"):wikitext(type(v)))
end
end -- for k, v
end -- for k, v
if lapsus then

s = string.format("params.<code>%s</code>.aliases", access)
-- label
Fault(factory("invalid-value"):gsub("$1", s))
sort = param.label or access
if sort:match( "^%d+$" ) then
legal = false
begin:attr( "data-sort-value",
string.format( "%05d", tonumber( sort ) ) )
end
end
end
begin:css( "font-weight", "bold" )
:wikitext( sort )


-- name and aliases
-- description etc.
s = fashioned(param)
code:css( "font-size", "92%" )
if s then
:css( "white-space", "nowrap" )
:wikitext( access )
desc:node(s)
end
if not fine( access ) then
if param.style then
code:addClass( "error" )
s = type(param.style)
Fault( string.format( "Bad ID params.<code>%s</code>", access ) )
legal = false
if s == "table" then
desc:css(param.style)
begin:attr( "data-sort-value", " " .. sort )
elseif s == "string" then
desc:cssText(param.style)
end
end
end
code = mw.html.create( "td" )
if param.suggestedvalues or param.default or param.example or param.autovalue then
:addClass( styles .. "name" )
local details = {
:node( code )
"suggestedvalues",
if access:match( "^%d+$" ) then
code:attr( "data-sort-value",
"default",
"example",
string.format( "%05d", tonumber( access ) ) )
end
"autovalue",
}
if type( param.aliases ) == "table" then
local dl = mw.html.create("dl")
local lapsus, syn
local dd, section, show
for k, v in pairs( param.aliases ) do
code:tag( "br" )
for i = 1, #details do
s = details[i]
if type( v ) == "string" then
show = param[s]
if not fine( v ) then
lapsus = true
if show then
code:node( mw.html.create( "span" )
dd = mw.html.create("dd")
section = factory("doc-param-" .. s)
:addClass( "error" )
if s == "default" and Config.support4default then
:css( "font-style", "italic" )
section = string.format("[[%s|%s]]", Config.support4default, mw.text.nowiki(section))
:wikitext( "string" ) )
:wikitext( s )
end
else
if param.type == "boolean" and (show == "0" or show == "1") then
syn = mw.html.create( "span" )
local boole = Permit.boole[(show == "1")]
if boole.lead == true then
:addClass( styles .. "alias" )
dd:node(mw.html.create("code"):wikitext(show)):wikitext(" ")
:css( "white-space", "nowrap" )
:wikitext( s )
end
code:node( syn )
if type(boole.show) == "string" then
local v = mw.html.create("span"):attr("aria-hidden", "true"):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
elseif s == "suggestedvalues" then
local v, css, class, ts = facilities(param)
if v then
local ul
ul, v = feasible(param, access, v)
if v then
dd:newline():node(ul)
if css then
dd:css(css)
if class then
dd:addClass(class)
end
end
else
if ts then
lapsus = true
dd:newline()
code:node( mw.html.create( "code" )
dd:node(ts)
:addClass( "error" )
end
:wikitext( type( v ) ) )
end
Data.params[access].suggestedvalues = v
end
end
end -- for k, v
end
if lapsus then
else
dd:wikitext(show)
s = string.format( "params.<code>%s</code>.aliases", access )
Fault( factory( "invalid-value" ):gsub( "$1", s ) )
legal = false
end
end
dl:node(mw.html.create("dt"):wikitext(section)):node(dd)
end
end
end -- i = 1, #details
desc:node(dl)
end


-- description etc.
-- type
if type(param.type) == "string" then
s = fashioned( param )
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"):addClass(styles .. "type")
if s then
if s then
desc:node( s )
if s == "string" then
Data.params[access].type = s
end
typed:wikitext(factory("doc-param-type-" .. s)):tag("br")
if param.style then
typed:node(mw.html.create("span"):addClass("error"):wikitext(param.type))
s = type( param.style )
if s == "table" then
Data.lasting = true
else
desc:css( param.style )
elseif s == "string" then
local support = Config["support4" .. param.type]
desc:cssText( param.style )
s = factory("doc-param-type-" .. param.type)
if support then
s = string.format("[[%s|%s]]", support, s)
end
end
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(factory("invalid-value"):gsub("$1", s))
legal = false
end
end
else
if param.suggestedvalues or
typed = mw.html.create("td"):wikitext(factory("doc-param-type-unknown"))
param.default or
Data.params[access].type = "unknown"
param.example or
param.autovalue then
if param.default then
Data.params[access].default = nil
local details = { "suggestedvalues",
Fault("Default value requires <code>type</code>")
"default",
legal = false
"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" )
:attr( "aria-hidden", "true" )
: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
elseif s == "suggestedvalues" then
local v, css, class, ts = facilities( param )
if v then
local ul
ul, v = feasible( param, access, v )
if v then
dd:newline()
:node( ul )
if css then
dd:css( css )
if class then
dd:addClass( class )
end
if ts then
dd:newline()
dd:node( ts )
end
end
Data.params[ access ].suggestedvalues = v
end
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
end
end

typed:addClass("navigation-not-searchable")
-- type
-- status
if type( param.type ) == "string" then
if param.required then
param.type = mw.text.trim( param.type )
if param.type == "" then
mode = 1
param.type = false
if param.autovalue then
Fault(string.format("autovalued <code>%s</code> required", access))
end
legal = false
end
end
if param.type then
if param.default then
Fault(string.format("Defaulted <code>%s</code> required", access))
s = Permit.types[ param.type ]
typed = mw.html.create( "td" )
legal = false
:addClass( styles .. "type" )
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|%s]]", support, s )
end
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( factory( "invalid-value" ):gsub( "$1", s ) )
legal = false
end
else
typed = mw.html.create( "td" )
:wikitext( factory( "doc-param-type-unknown" ) )
Data.params[ access ].type = "unknown"
if param.default then
Data.params[ access ].default = nil
Fault( "Default value requires <code>type</code>" )
legal = false
end
end
end
if param.deprecated then
typed:addClass( "navigation-not-searchable" )
Fault(string.format("Required deprecated <code>%s</code>", access))
-- status
legal = false
if param.required then
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
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
end
elseif param.deprecated then
status = ranking[ mode ]
mode = 4
ranking = factory( "doc-param-status-" .. status )
elseif param.suggested then
if mode == 1 or mode == 4 then
mode = 2
ranking = mw.html.create( "span" )
else
:css( "font-weight", "bold" )
mode = 3
:wikitext( ranking )
end
if type( param.deprecated ) == "string" then
ranking:tag( "br" )
status = ranking[mode]
ranking = factory("doc-param-status-" .. status)
ranking:wikitext( param.deprecated )
end
if mode == 1 or mode == 4 then
ranking = mw.html.create("span"):css("font-weight", "700"):wikitext(ranking)
if param.suggested and mode == 4 then
if type(param.deprecated) == "string" then
s = string.format( "Suggesting deprecated <code>%s</code>",
ranking:tag("br")
access )
ranking:wikitext(param.deprecated)
Fault( s )
end
legal = false
if param.suggested and mode == 4 then
end
s = string.format("Suggesting deprecated <code>%s</code>", access)
Fault(s)
legal = false
end
end
end
eager:attr( "data-sort-value", tostring( mode ) )
eager
:node( ranking )
:attr("data-sort-value", tostring(mode))
:addClass( string.format( "%sstatus-%s %s",
:node(ranking)
styles, status,
"navigation-not-searchable" ) )
:addClass(string.format("%sstatus-%s %s", styles, status, "navigation-not-searchable"))


-- <tr>
-- <tr>
r:attr( "id", "templatedata:" .. mw.uri.anchorEncode( access ) )
r:attr("id", "templatedata:" .. mw.uri.anchorEncode(access))
:css( Permit.css[ status ] )
:css(Permit.css[status])
:addClass( styles .. status )
:addClass(styles .. status)
:node( begin )
:node(begin)
:node( code )
:node(code)
:node( desc )
:node(desc)
:node( typed )
:node(typed)
:node( eager )
:node(eager)
:newline()
:newline()
if not legal then
if not legal then
r:css( "border", "#FF0000 3px solid" )
r:css("border", "3px solid #f00")
end
end
return r
return r
end -- feature()
end -- feature()




local function features()
local function features()
-- Create <table> for parameters
-- Create <table> for parameters.
-- Returns <table>, or nil
-- Returns <table>, or nil.
local r
local r
if Data.tree and Data.tree.params then
if Data.tree and Data.tree.params then
local tbl = mw.html.create( "table" )
local tbl = mw.html.create("table")
local tr = mw.html.create( "tr" )
local tr = mw.html.create("tr")
feat()
feat()
if Data.order and #Data.order > 1 then
if Data.order and #Data.order > 1 then
tbl:addClass( "sortable" )
tbl:addClass("sortable")
end
end
if type( Config.classTable ) == "table" then
if type(Config.classTable) == "table" then
for k, v in pairs( Config.classTable ) do
for k, v in pairs(Config.classTable) do
tbl:addClass( v )
tbl:addClass(v)
end -- for k, v
end -- for k, v
end
end
if type( Config.cssTable ) == "table" then
if type(Config.cssTable) == "table" then
tbl:css( Config.cssTable )
tbl:css(Config.cssTable)
end
end
tr:addClass( "navigation-not-searchable" )
tr:addClass("navigation-not-searchable")
:node( mw.html.create( "th" )
:node(
:attr( "colspan", "2" )
mw.html.create("th"):attr("colspan", "2"):css(Permit.css.tablehead):wikitext(factory("doc-param-name"))
)
:css( Permit.css.tablehead )
:wikitext( factory( "doc-param-name" ) ) )
:node(mw.html.create("th"):css(Permit.css.tablehead):wikitext(factory("doc-param-desc")))
:node( mw.html.create( "th" )
:node(mw.html.create("th"):css(Permit.css.tablehead):wikitext(factory("doc-param-type")))
:css( Permit.css.tablehead )
:node(mw.html.create("th"):css(Permit.css.tablehead):wikitext(factory("doc-param-status")))
tbl
:wikitext( factory( "doc-param-desc" ) ) )
:node( mw.html.create( "th" )
:newline()
:css( Permit.css.tablehead )
-- :node( mw.html.create( "thead" )
:node(tr)
:wikitext( factory( "doc-param-type" ) ) )
:node( mw.html.create( "th" )
-- )
:newline()
:css( Permit.css.tablehead )
if Data.order then
:wikitext( factory( "doc-param-status" ) ) )
tbl:newline()
local leave, s
-- :node( mw.html.create( "thead" )
for i = 1, #Data.order do
:node( tr )
s = Data.order[i]
-- )
if s:sub(1, 1) == "=" then
:newline()
leave = true
if Data.order then
tbl:node(fatten(s))
local leave, s
Data.order[i] = false
for i = 1, #Data.order do
elseif s:match("[=|]") then
Fault(string.format("Bad param <code>%s</code>", s))
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
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
else
r = tbl
tbl:node(feature(s))
end
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
end
if Config.cssTabWrap or Data.scroll then
return r
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()
end -- features()


local function fellow(any, assigned, at)

-- Check sets[] parameter and issue error message, if necessary.

-- Parameter:
local function fellow( any, assigned, at )
-- any -- should be number
-- Check sets[] parameter and issue error message, if necessary
-- assigned -- parameter name
-- Parameter:
-- any -- should be number
-- at -- number, of set
local s
-- assigned -- parameter name
-- at -- number, of set
if type(any) ~= "number" then
s = "<code>sets[%d].params[%s]</code>??"
local s
Fault(string.format(s, at, mw.text.nowiki(tostring(any))))
if type( any ) ~= "number" then
elseif type(assigned) == "string" then
s = "<code>sets[%d].params[%s]</code>??"
if not Data.got.params[assigned] then
Fault( string.format( s,
s = "<code>sets[%d].params %s</code> is undefined"
at,
Fault(string.format(s, at, assigned))
mw.text.nowiki( tostring( any ) ) ) )
elseif type( assigned ) == "string" then
if not Data.got.params[ assigned ] then
s = "<code>sets[%d].params %s</code> is undefined"
Fault( string.format( s, at, assigned ) )
end
else
s = "<code>sets[%d].params[%d] = %s</code>??"
Fault( string.format( s, k, type( assigned ) ) )
end
end
else
s = "<code>sets[%d].params[%d] = %s</code>??"
Fault(string.format(s, k, type(assigned)))
end
end -- fellow()
end -- fellow()




local function fellows()
local function fellows()
-- Check sets[] and issue error message, if necessary
-- Check sets[] and issue error message, if necessary.
local s
local s
if type( Data.got.sets ) == "table" then
if type(Data.got.sets) == "table" then
if type( Data.got.params ) == "table" then
if type(Data.got.params) == "table" then
for k, v in pairs( Data.got.sets ) do
for k, v in pairs(Data.got.sets) do
if type( k ) == "number" then
if type(k) == "number" then
if type( v ) == "table" then
if type(v) == "table" then
for ek, ev in pairs( v ) do
for ek, ev in pairs(v) do
if ek == "label" then
if ek == "label" then
s = type( ev )
s = type(ev)
if s ~= "string" and
if s ~= "string" and s ~= "table" then
s ~= "table" then
s = "<code>sets[%d].label</code>??"
s = "<code>sets[%d].label</code>??"
Fault(string.format(s, k))
Fault( string.format( s, k ) )
end
elseif ek == "params" and
type( ev ) == "table" then
for pk, pv in pairs( ev ) do
fellow( pk, pv, k )
end -- for pk, pv
else
ek = mw.text.nowiki( tostring( ek ) )
s = "<code>sets[%d][%s]</code>??"
Fault( string.format( s, k, ek ) )
end
end -- for ek, ev
else
k = mw.text.nowiki( tostring( k ) )
v = mw.text.nowiki( tostring( v ) )
s = string.format( "<code>sets[%s][%s]</code>??",
k, v )
Fault( s )
end
else
k = mw.text.nowiki( tostring( k ) )
s = string.format( "<code>sets[%s]</code> ?????", k )
Fault( s )
end
end
end -- for k, v
elseif ek == "params" and type(ev) == "table" then
for pk, pv in pairs(ev) do
fellow(pk, pv, k)
end -- for pk, pv
else
ek = mw.text.nowiki(tostring(ek))
s = "<code>sets[%d][%s]</code>??"
Fault(string.format(s, k, ek))
end
end -- for ek, ev
else
k = mw.text.nowiki(tostring(k))
v = mw.text.nowiki(tostring(v))
s = string.format("<code>sets[%s][%s]</code>??", k, v)
Fault(s)
end
else
else
k = mw.text.nowiki(tostring(k))
s = "<code>params</code> required for <code>sets</code>"
Fault( s )
s = string.format("<code>sets[%s]</code> ?????", k)
Fault(s)
end
end
end -- for k, v
else
else
s = "<code>sets</code> needs to be of <code>object</code> type"
s = "<code>params</code> required for <code>sets</code>"
Fault( s )
Fault(s)
end
end
else
s = "<code>sets</code> needs to be of <code>object</code> type"
Fault(s)
end
end -- fellows()
end -- fellows()


local function finalize(advance)

-- Wrap presentation into frame.

-- Parameter:
local function finalize( advance )
-- advance -- true, for nice
-- Wrap presentation into frame
-- Parameter:
-- Returns string.
local r, lapsus
-- advance -- true, for nice
if Data.div then
-- Returns string
r = tostring(Data.div)
local r, lapsus
if Data.div then
elseif Data.strip then
r = tostring( Data.div )
r = Data.strip
else
elseif Data.strip then
r = Data.strip
lapsus = true
else
r = ""
end
lapsus = true
r = ""
r = r .. failures()
if Data.source then
local live = (advance or lapsus)
if not live then
live = TemplateData.frame:preprocess("{{REVISIONID}}")
live = (live == "")
end
end
r = r .. failures()
if live then
r = r .. fancy(advance, lapsus)
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
end
return r
return r
end -- finalize()
end -- finalize()




local function find()
local function find()
-- Find JSON data within page source (title)
-- Find JSON data within page source (title).
-- Returns string, or nil
-- Returns string, or nil.
local s = Data.title:getContent()
local s = Data.title:getContent()
local i, j = s:find( "<templatedata>", 1, true )
local i, j = s:find("<templatedata>", 1, true)
local r
local r
if i then
if i then
local k = s:find( "</templatedata>", j, true )
local k = s:find("</templatedata>", j, true)
if k then
if k then
r = mw.text.trim( s:sub( j + 1, k - 1 ) )
r = mw.text.trim(s:sub(j + 1, k - 1))
end
end
end
end
return r
return r
end -- find()
end -- find()


local function flat(adjust)

-- Remove formatting from text string for VisualEditor.

-- Parameter:
local function flat( adjust )
-- Remove formatting from text string for VE
-- arglist -- string, to be stripped, or nil
-- Returns string, or nil.
-- Parameter:
local r
-- arglist -- string, to be stripped, or nil
if adjust then
-- Returns string, or nil
r = adjust:gsub("\n", " ")
local r
if adjust then
if r:find("<noexport>", 1, true) then
r = adjust:gsub( "\n", " " )
r = r:gsub("<noexport>.*</noexport>", "")
end
if r:find( "<noexport>", 1, true ) then
r = r:gsub( "<noexport>.*</noexport>", "" )
if r:find("<exportonly>", 1, true) then
end
r = r:gsub("</?exportonly>", "")
end
if r:find( "<exportonly>", 1, true ) then
r = r:gsub( "</?exportonly>", "" )
if r:find("''", 1, true) then
r = r:gsub("'''", ""):gsub("''", "")
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( "<br */?>", "\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 )
if r:find( "­", 1, true ) then
r = r:gsub( "­", "" )
end
end
end
end
if r:find("<", 1, true) then
return r
local Text = Fetch("Text")
r = r:gsub("<br */?>", "\r\n"):gsub("<sup>2</sup>", "&sup2;"):gsub("<sup>3</sup>", "&sup3;")
r = Text.getPlain(r)
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)
if r:find("&shy;", 1, true) then
r = r:gsub("&shy;", "")
end
end
end
return r
end -- flat()
end -- flat()




local function flush()
local function flush()
-- JSON encode narrowed input; obey unnamed (numerical) parameters
-- JSON encode narrowed input; obey unnamed (numerical) parameters.
-- Returns <templatedata> JSON string
-- 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
local r
if Data.tag then
if at then
r = mw.text.jsonEncode( Data.tag ):gsub( "%}$", "," )
r = string.format("<code>params.%s</code>", at)
else
else
r = "{"
r = "''root''"
end
end
if a then
r = r .. "\n\"params\":{"
r = string.format("%s<code>.%s</code>", r, a)
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
end
r = r .. "\n}\n}"
return r
return r
end
end -- flush()
local parent

if access then

parent = Data.got.params[access]

else
local function focus( access )
parent = Data.got
-- Check components; focus multilingual description, build trees
end
-- Parameter:
if type(parent) == "table" then
-- access -- string, name of parameter, nil for root
local f = function ( a, at )
local elem, permit, s, scope, slot, tag, target
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
if access then
parent = Data.got.params[ access ]
permit = Permit.params
if type(access) == "number" then
slot = tostring(access)
else
slot = access
end
else
else
parent = Data.got
permit = Permit.root
end
end
if type( parent ) == "table" then
for k, v in pairs(parent) do
local elem, got, permit, s, scope, slot, tag, target
scope = permit[k]
if access then
if scope then
permit = Permit.params
s = type(v)
if type( access ) == "number" then
if s == "string" and k ~= "format" then
slot = tostring( access )
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("&#13;" .. string.char(10), "<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
s = "Type <code>boolean</code> 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
else
slot = access
elem = v
end
end
else
end
permit = Permit.root
if type(elem) ~= "nil" then
end
if not target then
for k, v in pairs( parent ) do
if access then
scope = permit[ k ]
if not Data.tree.params then
if scope then
Data.tree.params = {}
s = type( v )
if s == "string" and k ~= "format" then
v = mw.text.trim( v )
end
end
if scope:find( s, 1, true ) then
Data.tree.params[slot] = {}
if scope:find( "I18N", 1, true ) then
target = Data.tree.params[slot]
if s == "string" then
else
elem = fair( v )
Data.tree = {}
elseif s == "table" then
target = Data.tree
local translated
end
v, translated = faraway( v )
end
if v then
target[k] = elem
if translated and
elem = false
end
k == "description" then
if type(v) ~= "nil" then
elem = { [ 1 ] = fair( v ),
[ 2 ] = translated }
if not tag then
else
if access then
elem = fair( v )
if type(v) == "string" and v.sub(1, 1) == "=" then
end
v = nil
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
s = "Type <code>boolean</code> 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" and
k ~= "suggestedvalues" then
tag[ k ] = v
end
end
else
else
s = string.format( "Type <code>%s</code> bad for %s",
if not Data.params then
scope, f( k, slot ) )
Data.params = {}
Fault( s )
end
Data.params[slot] = {}
tag = Data.params[slot]
end
end
else
else
Fault( "Unknown component " .. f( k, slot ) )
Data.tag = {}
tag = Data.tag
end
end
end
if type(v) ~= "nil" and k ~= "suggestedvalues" then
end -- for k, v
if not access and Data.got.sets then
tag[k] = v
fellows()
end
end
else
s = string.format("Type <code>%s</code> bad for %s", scope, f(k, slot))
Fault(s)
end
end
else
else
Fault( f() .. " needs to be of <code>object</code> type" )
Fault("Unknown component " .. f(k, slot))
end
end -- for k, v
if not access and Data.got.sets then
fellows()
end
end
else
Fault(f() .. " needs to be of <code>object</code> type")
end
end -- focus()
end -- focus()




local function format()
local function format()
-- Build formatted element
-- Build formatted element.
-- Returns <inline>
-- Returns <inline>.
local source = Data.tree.format:lower()
local source = Data.tree.format:lower()
local r, s
local r, s
if source == "inline" or source == "block" then
if source == "inline" or source == "block" then
r = mw.html.create( "i" )
r = mw.html.create("i"):wikitext(source)
else
:wikitext( source )
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", "&#92;n")
s = tostring(mw.html.create("code"):wikitext(s))
Fault("Invalid format " .. s)
source = false
end
else
else
local words = mw.text.split(source, "%s+")
local code
if source:find( "|", 1, true ) then
local show, start, support, unknown
for i = 1, #words do
local scan = "^[\n ]*%{%{[\n _]*|[\n _]*=[\n _]*%}%}[\n ]*$"
if source:match( scan ) then
s = words[i]
code = source:gsub( "\n", "N" )
if i == 1 then
else
start = s
end
s = mw.text.nowiki( source ):gsub( "\n", "\n" )
s = tostring( mw.html.create( "code" )
support = Permit.builder[s]
if support == start or support == "*" then
:wikitext( s ) )
Fault( "Invalid format " .. s )
Permit.builder[s] = true
elseif s:match("^[1-9]%d?") and Permit.builder.align then
source = false
end
Permit.builder.align = tonumber(s)
else
else
if unknown then
local words = mw.text.split( source, "%s+" )
local show, start, support, unknown
unknown = string.format("%s %s", unknown, s)
for i = 1, #words do
else
s = words[ i ]
unknown = s
if i == 1 then
end
start = s
end
end
end -- i = 1, #words
if unknown then
support = Permit.builder[ s ]
s = tostring(mw.html.create("code"):css("white-space", "nowrap"):wikitext(s))
if support == start or
Fault("Unknown/misplaced format keyword " .. s)
support == "*" then
Permit.builder[ s ] = true
source = false
start = false
elseif s:match( "^[1-9]%d?" ) and
end
Permit.builder.align then
if start == "inline" then
Permit.builder.align = tonumber( s )
else
if Permit.builder.half == true then
if unknown then
show = "inline half"
unknown = string.format( "%s %s", unknown, s )
code = "{{_ |_=_}}"
else
elseif Permit.builder.grouped == true then
unknown = s
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
end
end -- i = 1, #words
end -- for k, v
if unknown then
end
s = tostring( mw.html.create( "code" )
else
:css( "white-space", "nowrap" )
n = Permit.builder.align
:wikitext( s ) )
if type(n) == "number" and n > 1 then
Fault( "Unknown/misplaced format keyword " .. s )
s = string.format("%s %d", s, n)
source = false
else
start = false
n = 0 -- How come?
end
end
end
if start == "inline" then
if n > 1 then
if Permit.builder.half == true then
spaced = string.rep("_", n - 1) .. " "
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 - 1 ) .. " "
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
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
end
code = string.format("N{{_N%s|%s_%s=%s_%s}}N", start, space, spaced, spacer, suffix)
if code then
source = code:gsub( "N", "\n" )
if show == "block" then
code = mw.text.nowiki( code ):gsub( "N", "\n" )
show = "block newlines"
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
end
if show then
r = mw.html.create("span"):wikitext(show)
end
end
end
if source and Data.tag then
if code then
Data.tag.format = source
source = code:gsub("N", "\n")
code = mw.text.nowiki(code):gsub("N", "&#92;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
end
return r
if source and Data.tag then
Data.tag.format = source
end
return r
end -- format()
end -- format()




local function formatter()
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 x = fashioned( Data.tree, true, r )
local x = fashioned(Data.tree, true, r)
local s
local s
if x then
if x then
r = x
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:addClass("navigation-not-searchable"):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()
if s then
if Data.leading then
if Data.leading then
local toc = mw.html.create( "div" )
r:node(mw.html.create("h" .. Config.nested):wikitext(factory("doc-params"))):newline()
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:addClass( "navigation-not-searchable" )
: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()
r:node(s)
end
if s then
if Data.leading then
if Data.shared then
r:node( mw.html.create( "h" .. Config.nested )
local global = mw.html.create("div"):attr("id", "templatedata-global")
local shift
:wikitext( factory( "doc-params" ) ) )
if TemplateData.ltr then
:newline()
end
shift = "right"
r:node( s )
else
shift = "left"
end
end
global:css("float", shift):wikitext(string.format("[[%s|%s]]", Data.shared, "Global"))
if Data.shared then
r:newline():node(global)
local global = mw.html.create( "div" )
end
:attr( "id", "templatedata-global" )
if Data.tree and Data.tree.format then
local shift
local e = format()
if TemplateData.ltr then
if e then
shift = "right"
else
local show = "Format"
if Config.supportFormat then
shift = "left"
show = string.format("[[%s|%s]]", Config.supportFormat, show)
end
end
global:css( "float", shift )
r:node(mw.html.create("p"):addClass("navigation-not-searchable"):wikitext(show .. ": "):node(e))
:wikitext( string.format( "[[%s|%s]]",
Data.shared, "Global" ) )
r:newline()
:node( global )
end
end
end
if Data.tree and Data.tree.format then
return r
local e = format()
if e then
local show = "Format"
if Config.supportFormat then
show = string.format( "[[%s|%s]]",
Config.supportFormat, show )
end
r:node( mw.html.create( "p" )
:addClass( "navigation-not-searchable" )
:wikitext( show .. ": " )
:node( e ) )
end
end
return r
end -- formatter()
end -- formatter()




local function free()
local function free()
-- Remove JSON comment lines
-- Remove JSON comment lines.
if Data.source:find( "//", 1, true ) then
if Data.source:find("//", 1, true) then
Data.source:gsub( "([{,\"'])(%s*\n%s*//.*\n%s*)([{},\"'])",
Data.source:gsub("([{,\"'])(%s*\n%s*//.*\n%s*)([{},\"'])", "%1%3")
end
"%1%3" )
end
end -- free()
end -- free()




local function full()
local function full()
-- Build survey table from JSON data, append invisible <templatedata>
-- Build survey table from JSON data, append invisible <templatedata>.
Data.div = mw.html.create( "div" )
Data.div = mw.html.create("div"):addClass("mw-templatedata-doc-wrap")
if Permit.css.bg then
:addClass( "mw-templatedata-doc-wrap" )
if Permit.css.bg then
Data.div:css(Permit.css.bg)
end
Data.div:css( Permit.css.bg )
if Permit.css.fg then
end
if Permit.css.fg then
Data.div:css(Permit.css.fg)
end
Data.div:css( Permit.css.fg )
end
focus()
if Data.tag then
focus()
if Data.tag then
if type(Data.got.params) == "table" then
if type( Data.got.params ) == "table" then
for k, v in pairs(Data.got.params) do
focus(k)
for k, v in pairs( Data.got.params ) do
focus( k )
end -- for k, v
end -- for k, v
if Data.heirs then
if Data.heirs then
fathers()
fathers()
end
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
end
end
if Data.less then
Data.div:node(formatter())
Fault( Config.solo )
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
end
if Data.lasting then
Fault("deprecated type syntax")
end
if Data.less then
Fault(Config.solo)
end
end -- full()
end -- full()


local function furnish(adapt, arglist)

-- Analyze transclusion.

-- Parameter:
local function furnish( adapt, arglist )
-- adapt -- table, #invoke parameters
-- Analyze transclusion
-- arglist -- table, template parameters
-- Parameter:
-- Returns string.
-- adapt -- table, #invoke parameters
local source
-- arglist -- table, template parameters
favorize()
-- Returns string
-- deprecated:
local source
for k, v in pairs(Config.basicCnf) do
favorize()
if adapt[k] and adapt[k] ~= "" then
-- deprecated:
Config[v] = adapt[k]
for k, v in pairs( Config.basicCnf ) do
if adapt[ k ] and adapt[ k ] ~= "" then
Config[ v ] = adapt[ k ]
end
end -- for k, v
if arglist.heading and arglist.heading:match( "^[3-6]$" ) then
Config.nested = arglist.heading
else
Config.nested = "2"
end
end
end -- for k, v
Config.loudly = faculty( arglist.debug or adapt.debug )
if arglist.heading and arglist.heading:match("^[3-6]$") then
Data.lazy = faculty( arglist.lazy ) and not Config.loudly
Data.leading = faculty( arglist.TOC )
Config.nested = arglist.heading
else
if Data.leading and arglist.TOCsibling then
Config.nested = "2"
Data.sibling = mw.text.trim( arglist.TOCsibling )
end
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
end
if arglist.lang then
if type(arglist.vertical) == "string" and arglist.vertical:match("^%d*%.?%d+[emprx]+$") then
Data.slang = arglist.lang:lower()
Data.scroll = arglist.vertical
elseif adapt.lang then
end
Data.slang = adapt.lang:lower()
if not source then
end
Data.title = mw.title.getCurrentTitle()
if arglist.JSON then
source = arglist.JSON
source = find()
if not source and not Data.title.text:match(Config.subpage) then
elseif arglist.Global then
local s = string.format(Config.suffix, Data.title.prefixedText)
source = TemplateData.getGlobalJSON( arglist.Global,
Data.title = mw.title.new(s)
arglist.Local )
elseif arglist[ 1 ] then
if Data.title.exists 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()
source = find()
if not source and
end
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
end
end
if not Data.lazy then
if not Data.title then
if not Data.lazy then
Data.title = mw.title.getCurrentTitle()
if not Data.title then
Data.title = mw.title.getCurrentTitle()
end
Data.lazy = Data.title.text:match( Config.subpage )
end
end
Data.lazy = Data.title.text:match(Config.subpage)
if type( source ) == "string" then
end
TemplateData.getPlainJSON( source )
if type(source) == "string" then
end
return finalize( faculty( arglist.source ) )
TemplateData.getPlainJSON(source)
end
return finalize(faculty(arglist.source))
end -- furnish()
end -- furnish()


Failsafe.failsafe = function(atleast)

-- Retrieve versioning and check for compliance.

-- Precondition:
Failsafe.failsafe = function ( atleast )
-- atleast -- string, with required version
-- Retrieve versioning and check for compliance
-- or wikidata|item|~|@ or false
-- Precondition:
-- Postcondition:
-- atleast -- string, with required version
-- Returns string -- with queried version/item, also if problem
-- or wikidata|item|~|@ or false
-- false -- if appropriate
-- Postcondition:
-- 2024-03-01
-- Returns string -- with queried version/item, also if problem
local since = atleast
-- false -- if appropriate
local last = (since == "~")
-- 2020-08-17
local since = atleast
local linked = (since == "@")
local last = ( since == "~" )
local link = (since == "item")
local linked = ( since == "@" )
local r
local link = ( since == "item" )
if last or link or linked or since == "wikidata" then
local r
local item = Failsafe.item
since = false
if last or link or linked or since == "wikidata" then
local item = Failsafe.item
if type(item) == "number" and item > 0 then
since = false
local suited = string.format("Q%d", item)
if type( item ) == "number" and item > 0 then
if link then
local suited = string.format( "Q%d", item )
r = suited
if link then
else
local entity = mw.wikibase.getEntity(suited)
if type(entity) == "table" then
local seek = Failsafe.serialProperty or "P348"
local vsn = entity:formatPropertyValues(seek)
if type(vsn) == "table" and type(vsn.value) == "string" and vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false
elseif linked then
if mw.title.getCurrentTitle().prefixedText == mw.wikibase.getSitelink(suited) then
r = false
else
r = suited
r = suited
end
else
else
local entity = mw.wikibase.getEntity( suited )
r = vsn.value
if type( entity ) == "table" then
local seek = Failsafe.serialProperty or "P348"
local vsn = entity:formatPropertyValues( seek )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
if last and vsn.value == Failsafe.serial then
r = false
elseif linked then
if mw.title.getCurrentTitle().prefixedText
== mw.wikibase.getSitelink( suited ) then
r = false
else
r = suited
end
else
r = vsn.value
end
end
end
end
end
end
end
end
end
elseif link then
r = false
end
end
end
if type( r ) == "nil" then
if not since or since <= Failsafe.serial then
if type(r) == "nil" then
r = Failsafe.serial
if not since or since <= Failsafe.serial then
else
r = Failsafe.serial
else
r = false
end
r = false
end
end
end
return r
return r
end -- Failsafe.failsafe()
end -- Failsafe.failsafe()


TemplateData.getGlobalJSON = function(access, adapt)

-- Retrieve TemplateData from a global repository (JSON).

-- Parameter:
TemplateData.getGlobalJSON = function ( access, adapt )
-- access -- string, with page specifier (on WikiMedia Commons)
-- Retrieve TemplateData from a global repository (JSON)
-- adapt -- JSON string or table with local overrides
-- Parameter:
-- Returns true, if succeeded.
-- access -- string, with page specifier (on WikiMedia Commons)
local plugin = Fetch("/global")
-- adapt -- JSON string or table with local overrides
local r
-- Returns true, if succeeded
local plugin = Fetch( "/global" )
if type(plugin) == "table" and type(plugin.fetch) == "function" then
local r
local s, got = plugin.fetch(access, adapt)
if type( plugin ) == "table" and
if got then
type( plugin.fetch ) == "function" then
Data.got = got
Data.order = got.paramOrder
local s, got = plugin.fetch( access, adapt )
if got then
Data.shared = s
Data.got = got
r = true
full()
Data.order = got.paramOrder
else
Data.shared = s
Fault(s)
r = true
full()
else
Fault( s )
end
end
end
end
return r
return r
end -- TemplateData.getGlobalJSON()
end -- TemplateData.getGlobalJSON()


TemplateData.getPlainJSON = function(adapt)

-- Reduce enhanced JSON data to plain text localized JSON.

-- Parameter:
TemplateData.getPlainJSON = function ( adapt )
-- Reduce enhanced JSON data to plain text localized JSON
-- adapt -- string, with enhanced JSON
-- Returns string, or not.
-- Parameter:
-- adapt -- string, with enhanced JSON
if type(adapt) == "string" then
-- local JSONutil = Fetch("JSONutil", true) -- DON'T check for Module:JSONutil. (doesn't exist in English WP, blocked from creation.)
-- Returns string, or not
local JSONutil = nil -- DO act as if Fetch() failed.
if type( adapt ) == "string" then
Data.source = adapt
Data.source = adapt
free()
free()
local lucky
if JSONutil then
local Multilingual = Fetch("Multilingual", true)
lucky, Data.got = pcall( mw.text.jsonDecode, Data.source )
local f
if type( Data.got ) == "table" then
full()
if Multilingual then
elseif not Data.strip then
f = Multilingual.i18n
end
local scream = type( Data.got )
Data.got = JSONutil.fetch(Data.source, true, f)
if scream == "string" then
else
scream = Data.got
else
local lucky
scream = "Data.got: " .. scream
lucky, Data.got = pcall(mw.text.jsonDecode, Data.source)
end
end
if type(Data.got) == "table" then
Fault( "fatal JSON error: " .. scream )
end
full()
elseif not Data.strip then
local scream = type(Data.got)
if scream == "string" then
scream = Data.got
else
scream = "Data.got: " .. scream
end
Fault("fatal JSON error: " .. scream)
end
end
end
return Data.slim
return Data.slim
end -- TemplateData.getPlainJSON()
end -- TemplateData.getPlainJSON()


TemplateData.test = function(adapt, arglist)

TemplateData.frame = mw.getCurrentFrame()

TemplateData.test = function ( adapt, arglist )
return furnish(adapt, arglist)
TemplateData.frame = mw.getCurrentFrame()
return furnish( adapt, arglist )
end -- TemplateData.test()
end -- TemplateData.test()


-- Export.
local p = {}


p.f = function(frame)

-- Template call.
-- Export
local lucky, r
local p = { }
TemplateData.frame = frame

lucky, r = pcall(furnish, frame.args, frame:getParent().args)
p.f = function ( frame )
if not lucky then
-- Template call
local lucky, r
Fault("INTERNAL: " .. r)
r = failures()
TemplateData.frame = frame
end
lucky, r = pcall( furnish, frame.args, frame:getParent().args )
return r
if not lucky then
Fault( "INTERNAL: " .. r )
r = failures()
end
return r
end -- p.f
end -- p.f


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


p.TemplateData = function ()
p.TemplateData = function()
-- Module interface
-- Module interface.
return TemplateData
return TemplateData
end
end

setmetatable(p, {
__call = function(func, ...)
setmetatable(p, nil)
return Failsafe
end,
})


return p
return p

Latest revision as of 02:01, 23 July 2026

Documentation for this module may be created at Module:Format TemplateData/doc

local TemplateData = {
  item = 46997995,
  serial = "2025-02-07",
  suite = "TemplateData"
}

local Failsafe = TemplateData

local Config = {
  -- Multiple option names mapped into unique internal fields.
  basicCnf = {
    catProblem = "strange",
    classMultiColumns = "selMultClm",
    classNoNumTOC = "suppressTOCnum",
    classTable = "classTable",
    cssParWrap = "cssTabWrap",
    cssParams = "cssTable",
    docpageCreate = "suffix",
    docpageDetect = "subpage",
    helpAliases = "supportAliases",
    helpBoolean = "support4boolean",
    helpContent = "support4content",
    helpDate = "support4date",
    helpDefault = "support4default",
    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",
    tStylesMultiColumns = "stylesMultClm",
    tStylesTOCnum = "stylesTOCnum"
  },
  classTable = { "wikitable" },  -- Classes for params table
  cssTable = false,              -- Styles for params table
  cssTabWrap = false,            -- Styles for params table wrapper
  debug = false,
  debugmultilang = "#c0c0c0",
  jsonDebug = "json-code-lint",  -- Class for jsonDebug tool
  loudly = false,                -- Show exported element, etc.
  solo = false,                  -- Complaint on missing description
  strange = false,               -- Title of maintenance category
  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
  jump = false,     -- source position at end of "params"
  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 = {
    bg = "var(--background-color-base, #fff)",
    deprecated = "#ffcbcb",
    fg = "var(--color-base, #000)",
    optional = "#eaecf0",
    required = "#eaf3ff",
    suggested = "#fff",
    tableheadbg = "var(--background-color-progressive-subtle, #b3b7ff)"
  },
  params = {
    aliases = "table",
    autovalue = "string",
    default = "string table I18N nowiki",
    deprecated = "boolean string I18N",
    description = "string table I18N",
    example = "string table I18N nowiki",
    inherits = "string",
    label = "string table I18N",
    required = "boolean",
    style = "string table",
    suggested = "boolean",
    suggestedvalues = "string table number boolean",
    type = "string"
  },
  root = {
    description = "string table I18N",
    format = "string",
    maps = "table",
    paramOrder = "table",
    params = "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,
    ["string/line"] = "line",
    ["string/wiki-page-name"] = "wiki-page-name",
    ["string/wiki-user-name"] = "wiki-user-name",
    ["unbalanced-wikitext"] = true,
    ["wiki-file-name"] = true,
    ["wiki-page-name"] = true,
    ["wiki-template-name"] = true,
    ["wiki-user-name"] = true
  }
}

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), 0)
    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, r, slice, source
  if not Data.jump then
    Data.jump = Data.source:find("params", 2)
    if Data.jump then
      Data.jump = Data.jump + 7
    else
      Data.jump = 1
    end
  end
  i, k = Data.source:find(seek, at + Data.jump)
  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 facilities(apply)
  -- Retrieve details of suggestedvalues.
  -- Parameter:
  --     apply  -- table, with plain or enhanced values
  --               .suggestedvalues  -- table|string|number, or more
  -- Returns
  --     1  -- table, with suggestedvalues
  --     2  -- table, with CSS map, or not
  --     3  -- string, with class, or not
  --     4  -- string, with templatestyles, or not
  local elements = apply.suggestedvalues
  local s = type(elements)
  local r1, r2, r3, r4
  if s == "table" then
    local values = elements.values
    if type(values) == "table" then
      r1 = values
      if type(elements.scroll) == "string" then
        r2 = r2 or {}
        r2.height = apply.scroll
        r2.overflow = "auto"
      end
      if type(elements.minwidth) == "string" then
        local s = type(elements.maxcolumns)
        r2 = r2 or {}
        r2["column-width"] = elements.minwidth
        if s == "string" or s == "number" then
          s = tostring(elements.maxcolumns)
          r2["column-count"] = s
        end
        if type(Config.selMultClm) == "string" then
          r3 = Config.selMultClm
        end
        if type(Config.stylesMultClm) == "string" then
          local src = Config.stylesMultClm .. "/styles.css"
          r4 = TemplateData.frame:extensionTag("templatestyles", nil, { src = src })
        end
      end
    elseif elements and elements ~= "" then
      r1 = elements
    end
  elseif s == "string" then
    s = mw.text.trim(about)
    if s ~= "" then
      r1 = {}
      table.insert(r1, { code = s })
    end
  elseif s == "number" then
    r1 = {}
    table.insert(r1, { code = tostring(elements) })
  end
  return r1, r2, r3, r4
end -- facilities()

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 argument 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 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 tags = {
    {
      start = "<noexport>",
      stop = "</noexport>",
    },
    {
      start = "<exportonly>",
      stop = "</exportonly>",
      l = false,
    },
  }
  local r = adjust
  local i, j, k, s, tag
  for m = 1, 2 do
    tag = tags[m]
    if r:find(tag.start, 1, true) then
      s = r
      r = ""
      i = 1
      tag.l = true
      j, k = s:find(tag.start, i, true)
      while j do
        if j > 1 then
          r = r .. f(s:sub(i, j - 1))
        end
        i = k + 1
        j, k = s:find(tag.stop, i, true)
        if j then
          if m == 1 then
            r = r .. s:sub(i, j - 1)
          end
          i = k + 1
          j, k = s:find(tag.start, i, true)
        else
          Fault("missing " .. tag.stop)
        end
      end -- while j
      r = r .. s:sub(i)
    elseif m == 1 then
      r = f(r)
    end
  end -- for m
  if tags[2].l then
    r = r:gsub("<exportonly>.*</exportonly>", "")
  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"] = "0.77em",
        ["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 ~= "" and type(k) == "string" 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 <code>lang=%s</code>", "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)
          :css("color", "inherit")
          :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 subheadline.
  -- 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()

local function fathers()
  -- Merge parameters 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 &#124; %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"] = "1.25em" }
  local l, cx = pcall(mw.loadData, TemplateData.frame:getTitle() .. "/config")
  local scripting, style
  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 = "&#x2610;",
    },
    [true] = {
      css = boole,
      lead = true,
      show = "&#x2611;",
    },
  }
  Permit.css = {}
  for k, v in pairs(Permit.colors) do
    if k == "tableheadbg" then
      k = "tablehead"
    end
    if k == "fg" then
      style = "color"
    else
      style = "background-color"
    end
    Permit.css[k] = {}
    Permit.css[k][style] = 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 feasible(all, at, about)
  -- Deal with suggestedvalues within parameter.
  -- Parameter:
  --     all    -- parameter details
  --               .default
  --               .type
  --     at     -- string, with parameter name
  --     about  -- suggestedvalues  -- table,
  --                                   value and possibly description
  --                                   table may have elements:
  --                                   .code    -- mandatory
  --                                   .label   -- table|string
  --                                   .support -- table|string
  --                                   .icon    -- string
  --                                   .class   -- table|string
  --                                   .css     -- table
  --                                   .style   -- string
  --                                   .less    -- true: suppress code
  -- Returns
  --     1: mw.html object <ul>
  --     2: sequence table with values, or nil
  local h = {}
  local e, r1, r2, s, v
  if #about > 0 then
    for i = 1, #about do
      e = about[i]
      s = type(e)
      if s == "table" then
        if type(e.code) == "string" then
          s = mw.text.trim(e.code)
          if s == "" then
            e = nil
          else
            e.code = s
          end
        else
          e = nil
          s = string.format("params.%s.%s[%d] %s", at, "suggestedvalues", i, "MISSING 'code:'")
        end
      elseif s == "string" then
        s = mw.text.trim(e)
        if s == "" then
          e = nil
          s = string.format("params.%s.%s[%d] EMPTY", at, "suggestedvalues", i)
          Fault(s)
        else
          e = { code = s }
        end
      elseif s == "number" then
        e = { code = tostring(e) }
      else
        s = string.format("params.%s.%s[%d] INVALID", at, "suggestedvalues", i)
        Fault(s)
        e = false
      end
      if e then
        v = v or {}
        table.insert(v, e)
        if h[e.code] then
          s = string.format("params.%s.%s REPEATED %s", at, "suggestedvalues", e.code)
          Fault(s)
        else
          h[e.code] = true
        end
      end
    end -- for i
  else
    Fault(string.format("params.%s.suggestedvalues %s", at, "NOT AN ARRAY"))
  end
  if v then
    local code, d, k, less, story, swift, t, u
    r1 = mw.html.create("ul")
    r2 = {}
    for i = 1, #v do
      u = mw.html.create("li")
      e = v[i]
      table.insert(r2, e.code)
      story = false
      less = (e.less == true)
      if not less then
        swift = e.code
        if e.support then
          local scream, support
          s = type(e.support)
          if s == "string" then
            support = e.support
          elseif s == "table" then
            support = faraway(e.support)
          else
            scream = "INVALID"
          end
          if support then
            s = mw.text.trim(support)
            if s == "" then
              scream = "EMPTY"
            elseif s:find("[%[%]|%<%>]") then
              scream = "BAD PAGE"
            else
              support = s
            end
          end
          if scream then
            s = string.format("params.%s.%s[%d].support %s", at, "suggestedvalues", i, scream)
            Fault(s)
          else
            swift = string.format("[[:%s|%s]]", support, swift)
          end
        end
        if all.type:sub(1, 5) == "wiki-" and swift == e.code then
          local rooms = {
            file = 6,
            temp = 10,
            user = 2,
          }
          local ns = rooms[all.type:sub(6, 9)] or 0
          t = mw.title.makeTitle(ns, swift)
          if t and t.exists then
            swift = string.format("[[:%s|%s]]", t.prefixedText, swift)
          end
        end
        if e.code == all.default then
          k = 800
        else
          k = 300
        end
        code =
          mw.html.create("code"):css("font-weight", tostring(k)):css("white-space", "nowrap"):wikitext(swift)
        u:node(code)
      end
      if e.class then
        s = type(e.class)
        if s == "string" then
          u:addClass(e.class)
        elseif s == "table" then
          for k, s in pairs(e.class) do
            u:addClass(s)
          end -- for k, s
        else
          s = string.format("params.%s.%s[%d].class INVALID", at, "suggestedvalues", i)
          Fault(s)
        end
      end
      if e.css then
        if type(e.css) == "table" then
          u:css(e.css)
        else
          s = string.format("params.%s.%s[%d].css INVALID", at, "suggestedvalues", i)
          Fault(s)
        end
      end
      if e.style then
        if type(e.style) == "string" then
          u:cssText(e.style)
        else
          s = string.format("params.%s.%s[%d].style INVALID", at, "suggestedvalues", i)
          Fault(s)
        end
      end
      if all.type == "wiki-file-name" and not e.icon then
        e.icon = e.code
      end
      if e.label then
        s = type(e.label)
        if s == "string" then
          s = mw.text.trim(e.label)
          if s == "" then
            s = string.format("params.%s.%s[%d].label %s", at, "suggestedvalues", i, "EMPTY")
            Fault(s)
          else
            story = s
          end
        elseif s == "table" then
          story = faraway(e.label)
        else
          s = string.format("params.%s.%s[%d].label INVALID", at, "suggestedvalues", i)
          Fault(s)
        end
      end
      s = false
      if type(e.icon) == "string" then
        t = mw.title.makeTitle(6, e.icon)
        if t and t.file.exists then
          local g = mw.html.create("span")
          s = string.format("[[%s|16px]]", t.prefixedText)
          g:attr("role", "presentation"):wikitext(s)
          s = tostring(g)
        end
      end
      if not s and not less and e.label then
        s = mw.ustring.char(0x2013)
      end
      if s then
        d = mw.html.create("span"):wikitext(s)
        if TemplateData.ltr then
          if not less then
            d:css("margin-left", "0.5em")
          end
          if story then
            d:css("margin-right", "0.5em")
          end
        else
          if not less then
            d:css("margin-right", "0.5em")
          end
          if story then
            d:css("margin-left", "0.5em")
          end
        end
        u:node(d)
      end
      if story then
        u:wikitext(story)
      end
      r1:newline():node(u)
    end -- for i
  end
  if not r1 and v ~= false then
    Fault(string.format("params.%s.suggestedvalues INVALID", at))
    r1 = mw.html.create("code"):addClass("error"):wikitext("INVALID")
  end
  return r1, r2
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 = {}
      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 <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 eager = 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 styles = "mw-templatedata-doc-param-"
  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", "700"):wikitext(sort)

  -- name and aliases
  code:css("font-size", "0.92em"):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"):addClass(styles .. "name"):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, syn
    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"))
            :wikitext(s)
        else
          if Config.supportAliases then
            s = string.format("[[%s|%s]]", Config.supportAliases, mw.text.nowiki(s))
          end
          syn = mw.html.create("span"):addClass(styles .. "alias"):css("white-space", "nowrap"):wikitext(s)
          code:node(syn)
        end
      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(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.suggestedvalues or param.default or param.example or param.autovalue then
    local details = {
      "suggestedvalues",
      "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 s == "default" and Config.support4default then
          section = string.format("[[%s|%s]]", Config.support4default, mw.text.nowiki(section))
        end
        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"):attr("aria-hidden", "true"):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
        elseif s == "suggestedvalues" then
          local v, css, class, ts = facilities(param)
          if v then
            local ul
            ul, v = feasible(param, access, v)
            if v then
              dd:newline():node(ul)
              if css then
                dd:css(css)
                if class then
                  dd:addClass(class)
                end
                if ts then
                  dd:newline()
                  dd:node(ts)
                end
              end
              Data.params[access].suggestedvalues = v
            end
          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"):addClass(styles .. "type")
    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|%s]]", support, s)
        end
        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(factory("invalid-value"):gsub("$1", s))
      legal = false
    end
  else
    typed = mw.html.create("td"):wikitext(factory("doc-param-type-unknown"))
    Data.params[access].type = "unknown"
    if param.default then
      Data.params[access].default = nil
      Fault("Default value requires <code>type</code>")
      legal = false
    end
  end
  typed:addClass("navigation-not-searchable")
  -- status
  if param.required then
    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
      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 = factory("doc-param-status-" .. status)
  if mode == 1 or mode == 4 then
    ranking = mw.html.create("span"):css("font-weight", "700"):wikitext(ranking)
    if type(param.deprecated) == "string" then
      ranking:tag("br")
      ranking:wikitext(param.deprecated)
    end
    if param.suggested and mode == 4 then
      s = string.format("Suggesting deprecated <code>%s</code>", access)
      Fault(s)
      legal = false
    end
  end
  eager
    :attr("data-sort-value", tostring(mode))
    :node(ranking)
    :addClass(string.format("%sstatus-%s %s", styles, status, "navigation-not-searchable"))

  -- <tr>
  r:attr("id", "templatedata:" .. mw.uri.anchorEncode(access))
    :css(Permit.css[status])
    :addClass(styles .. status)
    :node(begin)
    :node(code)
    :node(desc)
    :node(typed)
    :node(eager)
    :newline()
  if not legal then
    r:css("border", "3px solid #f00")
  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 tbl = mw.html.create("table")
    local tr = mw.html.create("tr")
    feat()
    if Data.order and #Data.order > 1 then
      tbl:addClass("sortable")
    end
    if type(Config.classTable) == "table" then
      for k, v in pairs(Config.classTable) do
        tbl:addClass(v)
      end -- for k, v
    end
    if type(Config.cssTable) == "table" then
      tbl:css(Config.cssTable)
    end
    tr:addClass("navigation-not-searchable")
      :node(
        mw.html.create("th"):attr("colspan", "2"):css(Permit.css.tablehead):wikitext(factory("doc-param-name"))
      )
      :node(mw.html.create("th"):css(Permit.css.tablehead):wikitext(factory("doc-param-desc")))
      :node(mw.html.create("th"):css(Permit.css.tablehead):wikitext(factory("doc-param-type")))
      :node(mw.html.create("th"):css(Permit.css.tablehead):wikitext(factory("doc-param-status")))
    tbl
      :newline()
      --         :node( mw.html.create( "thead" )
      :node(tr)
      --              )
      :newline()
    if Data.order then
      local leave, s
      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
      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 fellow(any, assigned, at)
  -- Check sets[] parameter and issue error message, if necessary.
  -- Parameter:
  --     any       -- should be number
  --     assigned  -- parameter name
  --     at        -- number, of set
  local s
  if type(any) ~= "number" then
    s = "<code>sets[%d].params[%s]</code>??"
    Fault(string.format(s, at, mw.text.nowiki(tostring(any))))
  elseif type(assigned) == "string" then
    if not Data.got.params[assigned] then
      s = "<code>sets[%d].params %s</code> is undefined"
      Fault(string.format(s, at, assigned))
    end
  else
    s = "<code>sets[%d].params[%d] = %s</code>??"
    Fault(string.format(s, k, type(assigned)))
  end
end -- fellow()

local function fellows()
  -- Check sets[] and issue error message, if necessary.
  local s
  if type(Data.got.sets) == "table" then
    if type(Data.got.params) == "table" then
      for k, v in pairs(Data.got.sets) do
        if type(k) == "number" then
          if type(v) == "table" then
            for ek, ev in pairs(v) do
              if ek == "label" then
                s = type(ev)
                if s ~= "string" and s ~= "table" then
                  s = "<code>sets[%d].label</code>??"
                  Fault(string.format(s, k))
                end
              elseif ek == "params" and type(ev) == "table" then
                for pk, pv in pairs(ev) do
                  fellow(pk, pv, k)
                end -- for pk, pv
              else
                ek = mw.text.nowiki(tostring(ek))
                s = "<code>sets[%d][%s]</code>??"
                Fault(string.format(s, k, ek))
              end
            end -- for ek, ev
          else
            k = mw.text.nowiki(tostring(k))
            v = mw.text.nowiki(tostring(v))
            s = string.format("<code>sets[%s][%s]</code>??", k, v)
            Fault(s)
          end
        else
          k = mw.text.nowiki(tostring(k))
          s = string.format("<code>sets[%s]</code> ?????", k)
          Fault(s)
        end
      end -- for k, v
    else
      s = "<code>params</code> required for <code>sets</code>"
      Fault(s)
    end
  else
    s = "<code>sets</code> needs to be of <code>object</code> type"
    Fault(s)
  end
end -- fellows()

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("{{REVISIONID}}")
      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 for VisualEditor.
  -- 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("<exportonly>", 1, true) then
      r = r:gsub("</?exportonly>", "")
    end
    if r:find("''", 1, true) then
      r = r:gsub("'''", ""):gsub("''", "")
    end
    if r:find("<", 1, true) then
      local Text = Fetch("Text")
      r = r:gsub("<br */?>", "\r\n"):gsub("<sup>2</sup>", "&sup2;"):gsub("<sup>3</sup>", "&sup3;")
      r = Text.getPlain(r)
    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)
      if r:find("&shy;", 1, true) then
        r = r:gsub("&shy;", "")
      end
    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, 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("&#13;" .. string.char(10), "<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
                s = "Type <code>boolean</code> 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" and k ~= "suggestedvalues" then
              tag[k] = v
            end
          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
    if not access and Data.got.sets then
      fellows()
    end
  else
    Fault(f() .. " needs to be of <code>object</code> 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", "&#92;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 come?
              end
            end
            if n > 1 then
              spaced = string.rep("_", n - 1) .. " "
            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", "&#92;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 and Data.tag then
    Data.tag.format = source
  end
  return r
end -- format()

local function formatter()
  -- Build presented documentation.
  -- Returns <div>.
  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:addClass("navigation-not-searchable"):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
  s = features()
  if s then
    if Data.leading then
      r:node(mw.html.create("h" .. Config.nested):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|%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|%s]]", Config.supportFormat, show)
      end
      r:node(mw.html.create("p"):addClass("navigation-not-searchable"):wikitext(show .. ": "):node(e))
    end
  end
  return r
end -- formatter()

local function free()
  -- Remove JSON comment lines.
  if Data.source:find("//", 1, true) then
    Data.source:gsub("([{,\"'])(%s*\n%s*//.*\n%s*)([{},\"'])", "%1%3")
  end
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")
  if Permit.css.bg then
    Data.div:css(Permit.css.bg)
  end
  if Permit.css.fg then
    Data.div:css(Permit.css.fg)
  end
  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
  if arglist.heading and arglist.heading:match("^[3-6]$") then
    Config.nested = arglist.heading
  else
    Config.nested = "2"
  end
  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()

Failsafe.failsafe = function(atleast)
  -- Retrieve versioning and check for compliance.
  -- Precondition:
  --     atleast  -- string, with required version
  --                         or wikidata|item|~|@ or false
  -- Postcondition:
  --     Returns  string  -- with queried version/item, also if problem
  --              false   -- if appropriate
  -- 2024-03-01
  local since = atleast
  local last = (since == "~")
  local linked = (since == "@")
  local link = (since == "item")
  local r
  if last or link or linked or since == "wikidata" then
    local item = Failsafe.item
    since = false
    if type(item) == "number" and item > 0 then
      local suited = string.format("Q%d", item)
      if link then
        r = suited
      else
        local entity = mw.wikibase.getEntity(suited)
        if type(entity) == "table" then
          local seek = Failsafe.serialProperty or "P348"
          local vsn = entity:formatPropertyValues(seek)
          if type(vsn) == "table" and type(vsn.value) == "string" and vsn.value ~= "" then
            if last and vsn.value == Failsafe.serial then
              r = false
            elseif linked then
              if mw.title.getCurrentTitle().prefixedText == mw.wikibase.getSitelink(suited) then
                r = false
              else
                r = suited
              end
            else
              r = vsn.value
            end
          end
        end
      end
    elseif link then
      r = false
    end
  end
  if type(r) == "nil" then
    if not since or since <= Failsafe.serial then
      r = Failsafe.serial
    else
      r = false
    end
  end
  return r
end -- Failsafe.failsafe()

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)  -- DON'T check for Module:JSONutil. (doesn't exist in English WP, blocked from creation.)
    local JSONutil = nil -- DO act as if Fetch() failed.
    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
      local scream = type(Data.got)
      if scream == "string" then
        scream = Data.got
      else
        scream = "Data.got: " .. scream
      end
      Fault("fatal JSON error: " .. scream)
    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 Failsafe.failsafe(since) or ""
end -- p.failsafe

p.TemplateData = function()
  -- Module interface.
  return TemplateData
end

setmetatable(p, {
  __call = function(func, ...)
    setmetatable(p, nil)
    return Failsafe
  end,
})

return p