Módulo:TemplateData

Fonte: Enciclopédia de conhecimento da Igreja de Deus
Revisão em 01h30min de 14 de maio de 2024 por Jaewoo (discussão | contribs) (Criou a página com "local TemplateData = { suite = "TemplateData", serial = "2023-01-07", item = 46997995 } --[==[ improve template:TemplateData ]==] local Failsafe = TemplateData local Config = { -- multiple option names mapped into unique internal fields basicCnf = { catProblem = "strange", classMultiColumns = "selMultClm", classNoNumTOC = "suppressTOCnum", classT...")
(dif) ← Revisão anterior | Revisão atual (dif) | Revisão seguinte → (dif)
Saltar para a navegação Saltar para a pesquisa

A documentação para este módulo pode ser criada na página Módulo:TemplateData/doc

local TemplateData = { suite  = "TemplateData",
																							serial = "2023-01-07",
																							item   = 46997995 }
--[==[
improve template: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",
																	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
				debugmultilang = "C0C0C0",
				loudly         = false,    -- show exported element, etc.
				solo           = false,    -- complaint on missing description
				strange        = false,    -- title of maintenance category
				cssTable       = false,    -- styles for params table
				cssTabWrap     = false,    -- styles for params table wrapper
				debug          = false,
				subpage        = false,    -- pattern to identify subpage
				suffix         = false,    -- subpage creation scheme
				suppressTOCnum = false,    -- class for TOC number suppression
				jsonDebug      = "json-code-lint"    -- class for jsonDebug tool
}
local Data = {
				div     = false,    -- <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          = "FFFFFF",
																fg          = "000000",
																tableheadbg = "B3B7FF",
																required    = "EAF3FF",
																suggested   = "FFFFFF",
																optional    = "EAECF0",
																deprecated  = "FFCBCB" },
				params  = { aliases         = "table",
																autovalue       = "string",
																default         = "string table I18N nowiki",
																deprecated      = "boolean string I18N",
																description     = "string table I18N",
																example         = "string table I18N nowiki",
																label           = "string table I18N",
																inherits        = "string",
																required        = "boolean",
																style           = "string table",
																suggested       = "boolean",
																suggestedvalues = "string table number boolean",
																type            = "string" },
				root    = { description = "string table I18N",
																format      = "string",
																maps        = "table",
																params      = "table",
																paramOrder  = "table",
																sets        = "table" },
				search  = "[{,]%%s*(['\"])%s%%1%%s*:%%s*%%{",
				types   = { boolean                   = true,
																content                   = true,
																date                      = true,
																line                      = true,
																number                    = true,
																string                    = true,
																unknown                   = true,
																url                       = true,
																["wiki-file-name"]        = true,
																["wiki-page-name"]        = true,
																["wiki-template-name"]    = true,
																["wiki-user-name"]        = true,
																["unbalanced-wikitext"]   = true,
																["string/line"]           = "line",
																["string/wiki-page-name"] = "wiki-page-name",
																["string/wiki-user-name"] = "wiki-user-name" }
}
local function Fault( alert )
				-- Memorize error message
				-- Parameter:
				--     alert  -- string, error message
				if Data.scream then
								Data.scream = string.format( "%s *** %s", Data.scream, alert )
				else
								Data.scream = alert
				end
end -- Fault()
local function Fetch( ask, allow )
				-- Fetch module
				-- Parameter:
				--     ask    -- string, with name
				--                       "/global"
				--                       "JSONutil"
				--                       "Multilingual"
				--                       "Text"
				--                       "WLink"
				--     allow  -- true: no error if unavailable
				-- Returns table of module
				-- error: Module not available
				local sign = ask
				local r, stem
				if sign:sub( 1, 1 ) == "/" then
								sign = TemplateData.frame:getTitle() .. sign
				else
								stem = sign
								sign = "Module:" .. stem
				end
				if TemplateData.extern then
								r = TemplateData.extern[ sign ]
				else
								TemplateData.extern = { }
				end
				if not r then
								local lucky, g = pcall( require, sign )
								if type( g ) == "table" then
												if stem  and  type( g[ stem ] ) == "function" then
																r = g[ stem ]()
												else
																r = g
												end
												TemplateData.extern[ sign ] = r
								elseif not allow then
												error( string.format( "Fetch(%s) %s", sign, g ), 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
mw.log("facilities()",Config.stylesMultClm)
																if type( Config.stylesMultClm ) == "string" then
																				local src = Config.stylesMultClm .. "/styles.css"
																				r4 = TemplateData.frame
																																					:extensionTag( "templatestyles",
																																																				nil,
																																																				{ src = src } )
mw.log(r4)
																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 arg for boolean
				--     adjust  -- string or nil
				-- Returns boolean
				local s = type( adjust )
				local r
				if s == "string" then
								r = mw.text.trim( adjust )
								r = ( r ~= ""  and  r ~= "0" )
				elseif s == "boolean" then
								r = adjust
				else
								r = false
				end
				return r
end -- faculty()
local function failures()
				-- Retrieve error collection and category
				-- Returns string
				local r
				if Data.scream then
								local e = mw.html.create( "span" )
																									:addClass( "error" )
																									:wikitext( Data.scream )
								r = tostring( e )
								mw.addWarning( "'''TemplateData'''<br />" .. Data.scream )
								if Config.strange then
												r = string.format( "%s[[category:%s]]",
																															r,
																															Config.strange )
								end
				else
								r = ""
				end
				return r
end -- failures()
local function 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" ]   = "77%",
																				[ "line-height" ] = "1.35" }
												if alert then
																css.resize = "vertical"
												else
																css.display = "none"
												end
												r = mw.html.create( "pre" )
																							:addClass( support )
																							:css( css )
																							:wikitext( mw.text.encode( Data.source ) )
												r = tostring( r )
								end
								r = "\n".. r
				else
								r = ""
				end
				return r
end -- fancy()
local function faraway( alternatives )
				-- Retrieve best language version from multilingual text
				-- Parameter:
				--     alternatives  -- table, to be evaluated
				-- Returns
				--     1  -- string, with best match
				--     2  -- table of other versions, if any
				local n = 0
				local variants = { }
				local r1, r2
				for k, v in pairs( alternatives ) do
								if type( v ) == "string" then
												v = mw.text.trim( v )
												if v ~= ""  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 )
																														:node( plus )
												else
																plus:addClass( "templatedata-maintain" )
																				:css( "display", "none" )
												end
								end
				elseif Config.solo and asked then
								para:addClass( "error" )
												:wikitext( Config.solo )
								Data.less = true
				else
								para = false
				end
				if para then
								if plus then
												r = mw.html.create( "div" )
																							:node( para )
																							:node( plus )
								else
												r = para
								end
				end
				return r
end -- fashioned()
local function fatten( access )
				-- Create table row for sub-headline
				-- Parameter:
				--     access  -- string, with name
				-- Returns <tr>
				local param     = Data.tree.params[ access ]
				local sub, sort = access:match( "(=+)%s*(%S.*)$" )
				local headline  = mw.html.create( string.format( "h%d", #sub ) )
				local r         = mw.html.create( "tr" )
				local td        = mw.html.create( "td" )
																													:attr( "colspan", "5" )
																													:attr( "data-sort-value",  "!" .. sort )
				local s
				if param.style then
								s = type( param.style )
								if s == "table" then
												td:css( param.style )
								elseif s == "string" then
												td:cssText( param.style )
								end
				end
				s = fashioned( param, false, headline )
				if s then
								headline = s
				else
								headline:wikitext( sort )
				end
				td:node( headline )
				r:node( td )
				return r
end -- fatten()
local function fathers()
				-- Merge params with inherited values
				local n = 0
				local p = Data.params
				local t = Data.tree.params
				local p2, t2
				for k, v in pairs( Data.heirs ) do
								n = n + 1
				end -- for k, v
				for i = 1, n do
								if Data.heirs then
												for k, v in pairs( Data.heirs ) do
																if v  and  not Data.heirs[ v ] then
																				n               = n - 1
																				t[ k ].inherits = nil
																				Data.heirs[ k ] = nil
																				p2              = { }
																				t2              = { }
																				if p[ v ] then
																								for k2, v2 in pairs( p[ v ] ) do
																												p2[ k2 ] = v2
																								end -- for k2, v2
																								if p[ k ] then
																												for k2, v2 in pairs( p[ k ] ) do
																																if type( v2 ) ~= "nil" then
																																				p2[ k2 ] = v2
																																end
																												end -- for k2, v2
																								end
																								p[ k ] = p2
																								for k2, v2 in pairs( t[ v ] ) do
																												t2[ k2 ] = v2
																								end -- for k2, v2
																								for k2, v2 in pairs( t[ k ] ) do
																												if type( v2 ) ~= "nil" then
																																t2[ k2 ] = v2
																												end
																								end -- for k2, v2
																								t[ k ] = t2
																				else
																								Fault( "No params[] inherits " .. v )
																				end
																end
												end -- for k, v
								end
				end -- i = 1, n
				if n > 0 then
								local s
								for k, v in pairs( Data.heirs ) do
												if v then
																if s then
																				s = string.format( "%s &#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"] = "125%" }
				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 ] )
mw.log(k, v,s )
												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", "bold" )
									:wikitext( sort )
				-- name and aliases
				code:css( "font-size", "92%" )
								:css( "white-space", "nowrap" )
								:wikitext( access )
				if not fine( access ) then
								code:addClass( "error" )
								Fault( string.format( "Bad ID params.<code>%s</code>", access ) )
								legal = false
								begin:attr( "data-sort-value",  " " .. sort )
				end
				code = mw.html.create( "td" )
																		: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 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", "bold" )
																									: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", "#FF0000 3px solid" )
				end
				return r
end -- feature()
local function features()
				-- Create <table> for parameters
				-- Returns <table>, or nil
				local r
				if Data.tree and Data.tree.params then
								local 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 VE
				-- 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, got, permit, s, scope, slot, tag, target
								if access then
												permit = Permit.params
												if type( access ) == "number" then
																slot = tostring( access )
												else
																slot = access
												end
								else
												permit = Permit.root
								end
								for k, v in pairs( parent ) do
												scope = permit[ k ]
												if scope then
																s = type( v )
																if s == "string"  and  k ~= "format" then
																				v = mw.text.trim( v )
																end
																if scope:find( s, 1, true ) then
																				if scope:find( "I18N", 1, true ) then
																								if s == "string" then
																												elem = fair( v )
																								elseif s == "table" then
																												local translated
																												v, translated = faraway( v )
																												if v then
																																if translated  and
																																			k == "description" then
																																				elem = { [ 1 ] = fair( v ),
																																													[ 2 ] = translated }
																																else
																																				elem = fair( v )
																																end
																												else
																																elem = false
																												end
																								end
																								if type( v ) == "string" then
																												if k == "deprecated" then
																																if v == "1" then
																																				v = true
																																elseif v == "0" then
																																				v = false
																																end
																																elem = v
																												elseif scope:find( "nowiki", 1, true ) then
																																elem = mw.text.nowiki( v )
																																elem = elem:gsub( "&#13;\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
																				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 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
								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
				-- 2020-08-17
				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
								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 )
								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