Module:Format TemplateData: Difference between revisions
From Attu Project
More actions
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 = { |
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. |
|||
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", |
|||
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 |
|||
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 |
|||
cssTabWrap = false, -- Styles for params table wrapper |
|||
debug = false, |
|||
debugmultilang = "#c0c0c0", |
|||
subpage = false, -- pattern to identify subpage |
|||
jsonDebug = "json-code-lint", -- Class for jsonDebug tool |
|||
suffix = false, -- subpage creation scheme |
|||
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 = { |
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 = { |
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" |
|||
}, |
|||
required = "EAF3FF", |
|||
colors = { |
|||
suggested = "FFFFFF", |
|||
bg = "var(--background-color-base, #fff)", |
|||
optional = "EAECF0", |
|||
deprecated = "#ffcbcb", |
|||
fg = "var(--color-base, #000)", |
|||
params = { aliases = "table", |
|||
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", |
|||
aliases = "table", |
|||
autovalue = "string", |
|||
default = "string table I18N nowiki", |
|||
deprecated = "boolean string I18N", |
|||
description = "string table I18N", |
|||
example = "string table I18N nowiki", |
|||
type = "string" }, |
|||
inherits = "string", |
|||
label = "string table I18N", |
|||
required = "boolean", |
|||
style = "string 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, |
|||
boolean = 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 ) |
|||
-- |
-- 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 = alert |
|||
end |
|||
Data.scream = alert |
|||
end |
|||
end -- Fault() |
end -- Fault() |
||
local function Fetch(ask, allow) |
|||
-- Fetch module. |
|||
-- Parameter: |
|||
local function Fetch( ask, allow ) |
|||
-- |
-- ask -- string, with name |
||
-- "/global" |
|||
-- Parameter: |
|||
-- "JSONutil" |
|||
-- "Multilingual" |
|||
-- "Text" |
|||
-- "WLink" |
|||
-- allow -- true: no error if unavailable |
|||
-- Returns table of module |
|||
-- allow -- true: no error if unavailable |
|||
-- Error: Module not available |
|||
local sign = ask |
|||
-- error: Module not available |
|||
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 |
|||
stem = sign |
|||
stem |
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, 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 |
|||
if Multilingual and type(Multilingual.userLangCode) == "function" then |
|||
Data.slang = Multilingual.userLangCode() |
|||
else |
|||
type( Multilingual.userLangCode ) == "function" then |
|||
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 |
|||
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 ) |
|||
source = Data.source:sub(k + 1) |
|||
slice = source:match('^%s*"([^"]+)"s*:') |
|||
if not slice then |
|||
if not slice then |
|||
slice = source:match("^%s*'([^']+)'%s*:") |
|||
end |
|||
end |
|||
if ( slice and Permit.params[ slice ] ) or |
|||
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() |
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 |
|||
-- 1 -- table, with suggestedvalues |
|||
-- 2 -- table, with CSS map, or not |
|||
-- Returns |
|||
-- 3 -- string, with class, 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 |
local values = elements.values |
||
if |
if type(values) == "table" then |
||
r1 = values |
|||
if type(elements.scroll) == "string" then |
|||
r2 = r2 or {} |
|||
r2.height = apply.scroll |
|||
r2.overflow = "auto" |
|||
end |
|||
r2.height = apply.scroll |
|||
if type(elements.minwidth) == "string" then |
|||
local s = type(elements.maxcolumns) |
|||
r2 = r2 or {} |
|||
if type( elements.minwidth ) == "string" then |
|||
r2["column-width"] = elements.minwidth |
|||
if s == "string" or s == "number" then |
|||
s = tostring(elements.maxcolumns) |
|||
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 |
||
if type(Config.selMultClm) == "string" then |
|||
r3 = Config.selMultClm |
|||
if s ~= "" then |
|||
r1 = { } |
|||
table.insert( r1, |
|||
{ code = s } ) |
|||
end |
end |
||
if type(Config.stylesMultClm) == "string" then |
|||
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: |
|||
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. |
|||
-- adjust -- string or nil |
|||
-- Returns boolean. |
|||
local s = type(adjust) |
|||
local r |
|||
-- Returns boolean |
|||
if s == "string" then |
|||
r = mw.text.trim(adjust) |
|||
local r |
|||
r = (r ~= "" and r ~= "0") |
|||
elseif s == "boolean" then |
|||
r = mw.text.trim( adjust ) |
|||
r = adjust |
|||
else |
|||
elseif s == "boolean" then |
|||
r = false |
|||
end |
|||
return r |
|||
end |
|||
return r |
|||
end -- faculty() |
end -- faculty() |
||
local function failures() |
local function failures() |
||
-- 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 |
|||
r = tostring(e) |
|||
mw.addWarning("'''TemplateData'''<br />" .. Data.scream) |
|||
:addClass( "error" ) |
|||
if Config.strange then |
|||
:wikitext( Data.scream ) |
|||
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. |
|||
-- 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+", " " ) |
|||
start = "<noexport>", |
|||
stop = "</noexport>", |
|||
}, |
|||
stop = "</noexport>" }, |
|||
{ |
|||
{ start = "<exportonly>", |
|||
start = "<exportonly>", |
|||
stop = "</exportonly>", |
|||
l = false } |
|||
l = false, |
|||
}, |
|||
local r = adjust |
|||
} |
|||
local i, j, k, s, tag |
|||
local r = adjust |
|||
local i, j, k, s, tag |
|||
for m = 1, 2 do |
|||
if r:find( tag.start, 1, true ) then |
|||
tag = tags[m] |
|||
if r:find(tag.start, 1, true) then |
|||
r = "" |
|||
s = r |
|||
r = "" |
|||
i = 1 |
|||
j, k = s:find( tag.start, i, true ) |
|||
tag.l = true |
|||
j, k = s:find(tag.start, i, true) |
|||
while j do |
|||
r = r .. f( s:sub( i, j - 1 ) ) |
|||
if j > 1 then |
|||
r = r .. f(s:sub(i, j - 1)) |
|||
end |
|||
j, k = s:find( tag.stop, i, true ) |
|||
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 |
|||
j, k = s:find( tag.start, i, true ) |
|||
i = k + 1 |
|||
j, k = s:find(tag.start, i, true) |
|||
else |
|||
Fault("missing " .. tag.stop) |
|||
r = r .. s:sub( i ) |
|||
elseif m == 1 then |
|||
r = f( r ) |
|||
end |
end |
||
end -- |
end -- while j |
||
r = r .. s:sub(i) |
|||
elseif m == 1 then |
|||
r = r:gsub( "<exportonly>.*</exportonly>", "" ) |
|||
r = f(r) |
|||
end |
end |
||
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 ) |
|||
-- |
-- 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 |
local support = Config.jsonDebug |
||
local css |
|||
if Data.source then |
|||
if advance then |
|||
local support = Config.jsonDebug |
|||
css = { |
|||
height = "6em", |
|||
resize = "vertical", |
|||
} |
|||
resize = "vertical" } |
|||
r = { |
|||
[1] = "syntaxhighlight", |
|||
[2] = Data.source, |
|||
lang = "json", |
|||
style = table.concat(css, ";"), |
|||
} |
|||
r.class( support ) |
|||
if alert then |
|||
r.class(support) |
|||
r = TemplateData.frame:callParserFunction( "#tag", r ) |
|||
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 |
||
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 |
||
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 |
|||
-- Returns |
|||
-- 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 r1, r2 |
|||
for k, v in pairs(alternatives) do |
|||
local variants = { } |
|||
if type(v) == "string" then |
|||
local r1, r2 |
|||
v = mw.text.trim(v) |
|||
if v ~= "" and type(k) == "string" then |
|||
k = k:lower() |
|||
variants[k] = v |
|||
n = n + 1 |
|||
end |
|||
variants[ k ] = v |
|||
end |
|||
n = n + 1 |
|||
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 |
||
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)) |
|||
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: |
|||
-- assign -- <block>, if to be equipped |
|||
-- Returns <block> with head, or nil. |
|||
-- asked -- true, if mandatory description |
|||
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 |
|||
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: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 |
||
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 |
|||
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 |
|||
-- Returns <tr>. |
|||
local param = Data.tree.params[access] |
|||
-- access -- string, with name |
|||
local sub, sort = access:match("(=+)%s*(%S.*)$") |
|||
-- Returns <tr> |
|||
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 |
|||
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 |
|||
elseif s == "string" then |
|||
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 |
|||
headline = s |
|||
else |
|||
headline:wikitext( sort ) |
|||
headline:wikitext(sort) |
|||
end |
|||
end |
|||
td:node( headline ) |
|||
td:node(headline) |
|||
r:node(td) |
|||
return r |
|||
return r |
|||
end -- fatten() |
end -- fatten() |
||
local function fathers() |
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 |
||
end -- for |
end -- for k2, v2 |
||
end |
|||
p[k] = p2 |
|||
for k2, v2 in pairs(t[v]) do |
|||
t2[k2] = v2 |
|||
end -- for k2, v2 |
|||
for k2, v2 in pairs(t[k]) do |
|||
if type(v2) ~= "nil" then |
|||
t2[k2] = v2 |
|||
end |
|||
end -- for k2, v2 |
|||
t[k] = t2 |
|||
else |
|||
Fault("No params[] inherits " .. v) |
|||
end |
|||
end |
end |
||
end -- |
end -- for k, v |
||
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 | %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 boole = { ["font-size"] = "1.25em" } |
|||
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 |
|||
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 = "☐", |
|||
}, |
|||
[true] = { |
|||
css = boole, |
|||
lead = true, |
|||
show = "☑", |
|||
}, |
|||
} |
|||
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 |
||
style = "background-color" |
|||
end |
end |
||
Permit.css[k] = {} |
|||
boole[ "margin-" .. scripting ] = "3em" |
|||
Permit. |
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, |
|||
if type(cx.permit.boole) == "table" then |
|||
if type(cx.permit.boole[true]) == "table" then |
|||
Permit.css = { } |
|||
Permit.boole[false] = cx.permit.boole[false] |
|||
if k == "tableheadbg" then |
|||
k = "tablehead" |
|||
end |
end |
||
if |
if type(cx.permit.boole[true]) == "table" then |
||
Permit.boole[true] = cx.permit.boole[true] |
|||
else |
|||
style = "background-color" |
|||
end |
end |
||
end |
|||
if type(cx.permit.css) == "table" then |
|||
for k, v in pairs(cx.permit.css) do |
|||
if type( |
if type(v) == "table" then |
||
Permit.css[k] = v |
|||
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 |
||
for k, v in pairs(Config.basicCnf) do |
|||
type( |
s = type(cx[k]) |
||
if s == "string" or s == "table" then |
|||
local got = mw.message.new( "templatedata-doc-subpage" ) |
|||
Config[v] = cx[k] |
|||
end |
|||
if got:isDisabled() then |
|||
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 |
||
e = nil |
|||
s = string.format("params.%s.%s[%d] %s", at, "suggestedvalues", i, "MISSING 'code:'") |
|||
end |
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 |
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 |
end |
||
end -- |
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 |
|||
for i = 1, #v do |
|||
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 |
|||
if e.support then |
|||
local scream, support |
|||
-- .icon -- string |
|||
s = type(e.support) |
|||
-- .class -- table|string |
|||
if s == "string" then |
|||
-- .css -- table |
|||
support = e.support |
|||
elseif s == "table" then |
|||
-- .less -- true: suppress code |
|||
support = faraway(e.support) |
|||
-- Returns |
|||
else |
|||
scream = "INVALID" |
|||
end |
|||
if support then |
|||
s = mw.text.trim(support) |
|||
if #about > 0 then |
|||
if s == "" then |
|||
scream = "EMPTY" |
|||
s |
elseif s:find("[%[%]|%<%>]") 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 |
||
support = s |
|||
at, "suggestedvalues", i ) |
|||
Fault( s ) |
|||
e = false |
|||
end |
end |
||
end |
|||
if scream then |
|||
s = string.format("params.%s.%s[%d].support %s", at, "suggestedvalues", i, scream) |
|||
Fault(s) |
|||
else |
|||
s = string.format( "params.%s.%s REPEATED %s", |
|||
swift = string.format("[[:%s|%s]]", support, swift) |
|||
at, |
|||
end |
|||
"suggestedvalues", |
|||
end |
|||
e.code ) |
|||
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 |
|||
else |
|||
t = mw.title.makeTitle(ns, swift) |
|||
Fault( string.format( "params.%s.suggestedvalues %s", |
|||
if t and t.exists then |
|||
swift = string.format("[[:%s|%s]]", t.prefixedText, swift) |
|||
end |
|||
end |
|||
end |
|||
local code, d, k, less, story, swift, t, u |
|||
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) |
|||
story = false |
|||
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) |
|||
scream = "INVALID" |
|||
Fault(s) |
|||
end |
|||
end |
|||
s = mw.text.trim( support ) |
|||
if e.css then |
|||
if type(e.css) == "table" then |
|||
u:css(e.css) |
|||
else |
|||
scream = "BAD PAGE" |
|||
s = string.format("params.%s.%s[%d].css INVALID", at, "suggestedvalues", i) |
|||
else |
|||
Fault(s) |
|||
end |
|||
end |
|||
if e.style then |
|||
if type(e.style) == "string" then |
|||
u:cssText(e.style) |
|||
else |
|||
"suggestedvalues", |
|||
s = string.format("params.%s.%s[%d].style INVALID", at, "suggestedvalues", i) |
|||
i, |
|||
Fault(s) |
|||
end |
|||
end |
|||
if all.type == "wiki-file-name" and not e.icon then |
|||
swift = string.format( "[[:%s|%s]]", |
|||
e.icon = e.code |
|||
support, swift ) |
|||
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") |
|||
user = 2 } |
|||
Fault(s) |
|||
else |
|||
t = mw.title.makeTitle( ns, swift ) |
|||
story = s |
|||
end |
|||
swift = string.format( "[[:%s|%s]]", |
|||
elseif s == "table" then |
|||
t.prefixedText, swift ) |
|||
story = faraway(e.label) |
|||
else |
|||
s = string.format("params.%s.%s[%d].label INVALID", at, "suggestedvalues", i) |
|||
if e.code == all.default then |
|||
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 |
|||
:css( "white-space", "nowrap" ) |
|||
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 s == "string" then |
|||
if not s and not less and e.label then |
|||
s = mw.ustring.char(0x2013) |
|||
end |
|||
for k, s in pairs( e.class ) do |
|||
if s then |
|||
d = mw.html.create("span"):wikitext(s) |
|||
if TemplateData.ltr then |
|||
if not less then |
|||
s = string.format( "params.%s.%s[%d].class INVALID", |
|||
d:css("margin-left", "0.5em") |
|||
end |
|||
if story then |
|||
d:css("margin-right", "0.5em") |
|||
end |
|||
else |
|||
if type( e.css ) == "table" then |
|||
if not less then |
|||
d:css("margin-right", "0.5em") |
|||
end |
|||
s = string.format( "params.%s.%s[%d].css INVALID", |
|||
if story then |
|||
at, "suggestedvalues", i ) |
|||
d:css("margin-left", "0.5em") |
|||
end |
|||
end |
|||
u:node(d) |
|||
end |
|||
if type( e.style ) == "string" then |
|||
if story then |
|||
u:cssText( e.style ) |
|||
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 |
|||
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 |
|||
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. |
|||
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 |
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 |
|||
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 |
|||
legal = false |
|||
begin:attr( "data-sort-value", |
|||
string.format( "%05d", tonumber( sort ) ) ) |
|||
end |
end |
||
end |
|||
begin:css( "font-weight", "bold" ) |
|||
:wikitext( sort ) |
|||
-- description etc. |
|||
s = fashioned(param) |
|||
code:css( "font-size", "92%" ) |
|||
if s then |
|||
:css( "white-space", "nowrap" ) |
|||
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 ) ) |
|||
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 |
|||
"default", |
|||
"example", |
|||
string.format( "%05d", tonumber( access ) ) ) |
|||
"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 |
|||
for i = 1, #details do |
|||
s = details[i] |
|||
if type( v ) == "string" then |
|||
show = param[s] |
|||
if not fine( v ) then |
|||
if show then |
|||
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" ) ) |
|||
end |
|||
if param.type == "boolean" and (show == "0" or show == "1") then |
|||
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" ) |
|||
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 |
end |
||
if ts then |
|||
dd:newline() |
|||
dd:node(ts) |
|||
end |
|||
end |
|||
Data.params[access].suggestedvalues = v |
|||
end |
end |
||
end |
|||
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 |
|||
-- 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 |
||
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 ) |
|||
Data.lasting = true |
|||
else |
|||
desc:css( param.style ) |
|||
local support = Config["support4" .. param.type] |
|||
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 |
|||
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 ) |
|||
mode = 1 |
|||
if param.autovalue then |
|||
Fault(string.format("autovalued <code>%s</code> required", access)) |
|||
end |
|||
legal = false |
|||
end |
end |
||
if param. |
if param.default then |
||
Fault(string.format("Defaulted <code>%s</code> required", access)) |
|||
s = Permit.types[ param.type ] |
|||
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 |
|||
status = ranking[mode] |
|||
ranking = factory("doc-param-status-" .. status) |
|||
ranking:wikitext( param.deprecated ) |
|||
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, |
|||
: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() |
end -- feature() |
||
local function features() |
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")) |
|||
) |
|||
:css( Permit.css.tablehead ) |
|||
: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 |
|||
:wikitext( factory( "doc-param-desc" ) ) ) |
|||
:newline() |
|||
-- :node( mw.html.create( "thead" ) |
|||
:node(tr) |
|||
:wikitext( factory( "doc-param-type" ) ) ) |
|||
-- ) |
|||
:newline() |
|||
:css( Permit.css.tablehead ) |
|||
if Data.order then |
|||
:wikitext( factory( "doc-param-status" ) ) ) |
|||
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)) |
|||
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 |
||
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: |
|||
-- at -- number, of set |
|||
local s |
|||
-- assigned -- parameter name |
|||
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. |
|||
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)) |
|||
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 |
||
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>" |
|||
s = string.format("<code>sets[%s]</code> ?????", k) |
|||
Fault(s) |
|||
end |
end |
||
end -- for k, v |
|||
else |
else |
||
s = "<code>params</code> required for <code>sets</code>" |
|||
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 |
|||
-- Returns string. |
|||
local r, lapsus |
|||
-- advance -- true, for nice |
|||
if Data.div then |
|||
-- Returns string |
|||
r = tostring(Data.div) |
|||
local r, lapsus |
|||
elseif Data.strip then |
|||
r = Data.strip |
|||
else |
|||
elseif Data.strip then |
|||
lapsus = true |
|||
r = "" |
|||
end |
|||
lapsus = true |
|||
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 |
||
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). |
|||
-- 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 |
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 ) |
|||
-- 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 |
if r:find("<noexport>", 1, true) then |
||
r = r:gsub("<noexport>.*</noexport>", "") |
|||
end |
|||
if r:find( "<noexport>", 1, true ) then |
|||
if r:find("<exportonly>", 1, true) then |
|||
r = r:gsub("</?exportonly>", "") |
|||
end |
|||
if r:find( "<exportonly>", 1, true ) then |
|||
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>", "²"):gsub("<sup>3</sup>", "³") |
|||
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("­", 1, true) then |
|||
r = r:gsub("­", "") |
|||
end |
|||
end |
|||
end |
|||
return r |
|||
end -- flat() |
end -- flat() |
||
local function flush() |
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 |
local r |
||
if |
if at then |
||
r = string.format("<code>params.%s</code>", at) |
|||
else |
else |
||
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 |
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 |
||
permit = Permit.params |
|||
if type(access) == "number" then |
|||
slot = tostring(access) |
|||
else |
|||
slot = access |
|||
end |
|||
else |
else |
||
permit = Permit.root |
|||
end |
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(" " .. 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 |
||
elem = v |
|||
end |
end |
||
end |
|||
if type(elem) ~= "nil" then |
|||
if not target then |
|||
if access then |
|||
if not Data.tree.params then |
|||
Data.tree.params = {} |
|||
s = type( v ) |
|||
if s == "string" and k ~= "format" then |
|||
v = mw.text.trim( v ) |
|||
end |
end |
||
Data.tree.params[slot] = {} |
|||
target = Data.tree.params[slot] |
|||
else |
|||
Data.tree = {} |
|||
target = Data.tree |
|||
end |
|||
end |
|||
target[k] = elem |
|||
elem = false |
|||
end |
|||
k == "description" then |
|||
if type(v) ~= "nil" then |
|||
elem = { [ 1 ] = fair( v ), |
|||
if not tag then |
|||
if access then |
|||
if type(v) == "string" and v.sub(1, 1) == "=" then |
|||
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 |
||
if not Data.params then |
|||
Data.params = {} |
|||
end |
|||
Data.params[slot] = {} |
|||
tag = Data.params[slot] |
|||
end |
end |
||
else |
else |
||
Data.tag = {} |
|||
tag = Data.tag |
|||
end |
|||
end |
end |
||
if type(v) ~= "nil" and k ~= "suggestedvalues" then |
|||
end -- for k, v |
|||
tag[k] = v |
|||
end |
|||
end |
|||
else |
|||
s = string.format("Type <code>%s</code> bad for %s", scope, f(k, slot)) |
|||
Fault(s) |
|||
end |
end |
||
else |
else |
||
Fault( |
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. |
|||
-- 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 |
|||
: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", "\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 |
|||
local show, start, support, unknown |
|||
for i = 1, #words do |
|||
local scan = "^[\n ]*%{%{[\n _]*|[\n _]*=[\n _]*%}%}[\n ]*$" |
|||
s = words[i] |
|||
if i == 1 then |
|||
start = s |
|||
end |
|||
s = mw.text.nowiki( source ):gsub( "\n", "\n" ) |
|||
support = Permit.builder[s] |
|||
if support == start or support == "*" then |
|||
:wikitext( s ) ) |
|||
Permit.builder[s] = true |
|||
elseif s:match("^[1-9]%d?") and Permit.builder.align then |
|||
source = false |
|||
Permit.builder.align = tonumber(s) |
|||
else |
else |
||
if unknown then |
|||
local words = mw.text.split( source, "%s+" ) |
|||
unknown = string.format("%s %s", unknown, s) |
|||
else |
|||
unknown = 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 |
|||
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 ) |
|||
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 |
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 |
end |
||
if |
if n > 1 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 |
|||
if show == "block" then |
|||
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 |
if code then |
||
source = code:gsub("N", "\n") |
|||
code = mw.text.nowiki(code):gsub("N", "\n") |
|||
code = mw.html.create("code"):css("margin-left", "1em"):css("margin-right", "1em"):wikitext(code) |
|||
if r then |
|||
r = mw.html.create("span"):node(r):node(code) |
|||
else |
|||
r = code |
|||
end |
|||
end |
end |
||
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. |
|||
-- 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 |
||
end |
|||
s = features() |
|||
if s then |
|||
if Data.leading then |
if Data.leading then |
||
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 |
||
r:node(s) |
|||
end |
|||
if s then |
|||
if Data.shared then |
|||
local global = mw.html.create("div"):attr("id", "templatedata-global") |
|||
local shift |
|||
:wikitext( factory( "doc-params" ) ) ) |
|||
if TemplateData.ltr then |
|||
:newline() |
|||
shift = "right" |
|||
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" |
|||
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. |
|||
if Data.source:find("//", 1, true) then |
|||
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>. |
|||
Data.div = mw.html.create("div"):addClass("mw-templatedata-doc-wrap") |
|||
if Permit.css.bg then |
|||
:addClass( "mw-templatedata-doc-wrap" ) |
|||
Data.div:css(Permit.css.bg) |
|||
end |
|||
Data.div:css( Permit.css.bg ) |
|||
if Permit.css.fg then |
|||
end |
|||
Data.div:css(Permit.css.fg) |
|||
end |
|||
Data.div:css( Permit.css.fg ) |
|||
focus() |
|||
if Data.tag then |
|||
focus() |
|||
if Data. |
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 |
|||
end -- for k, v |
|||
if Data.heirs then |
|||
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 |
|||
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 = 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 ) |
|||
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() |
||
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.lazy then |
|||
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 |
|||
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 linked = (since == "@") |
|||
local link = (since == "item") |
|||
local r |
|||
if last or link or linked or since == "wikidata" then |
|||
local |
local item = Failsafe.item |
||
since = false |
|||
if last or link or linked or since == "wikidata" then |
|||
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 |
r = suited |
||
end |
|||
else |
else |
||
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 type(r) == "nil" then |
|||
if not since or since <= Failsafe.serial then |
|||
r = Failsafe.serial |
|||
else |
|||
r = false |
|||
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 |
|||
if type(plugin) == "table" and type(plugin.fetch) == "function" then |
|||
local |
local s, got = plugin.fetch(access, adapt) |
||
if |
if got then |
||
Data.got = got |
|||
Data.order = got.paramOrder |
|||
local s, got = plugin.fetch( access, adapt ) |
|||
Data.shared = s |
|||
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 ) |
|||
-- adapt -- string, with enhanced JSON |
|||
-- Returns string, or not. |
|||
-- Parameter: |
|||
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 |
|||
free() |
|||
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 |
|||
if Multilingual 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 |
|||
local lucky |
|||
lucky, Data.got = pcall(mw.text.jsonDecode, Data.source) |
|||
end |
|||
if type(Data.got) == "table" then |
|||
Fault( "fatal JSON error: " .. scream ) |
|||
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() |
|||
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 |
|||
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 |
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 |
||
end |
|||
if since then |
|||
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. |
|||
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 | %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 = "☐",
},
[true] = {
css = boole,
lead = true,
show = "☑",
},
}
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>", "²"):gsub("<sup>3</sup>", "³")
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("­", 1, true) then
r = r:gsub("­", "")
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(" " .. 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", "\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", "\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