Module:Test

-- Module for displaying a table with TemplateData information of -- a template that is much richer than the table that is displayed -- by the tags by default.

-- Originally based on https://en.wikipedia.org/wiki/Module:Format_TemplateData, -- heavily refactored for readability and maintainability, -- and adjusted to the needs of the Terraria Wiki.

local TemplateData = { serial = "2017-11-06", suite = "TemplateData" } local plaintext = require("Module:Plain text") local trim = mw.text.trim

local jsonsource = ""

- -- Global tables

---The keys in this table are the arguments from the module invocation that ---are recognized for modifying the internal ``Config`` table, the values ---are the respective keys of the ``Config`` table local ConfigAliases = { -- e.g. using "|cat=foo" in the #invoke will set Config.maintenanceCateName to "foo" cat = "maintenanceCateName", cate = "maintenanceCateName",

tocclass = "classForToc", classNoNumTOC = "classForToc",

tablecss = "stylesForParamstable", tablestyle = "stylesForParamstable", cssParams = "stylesForParamstable",

wrappercss = "stylesForParamstableWrapper", wrapperstyle = "stylesForParamstableWrapper", cssParWrap = "stylesForParamstableWrapper",

docpageCreate = "patternForCreatingSubpage",

docpageDetect = "patternForMatchingSubpage",

missingDescText = "textIfDescriptionIsMissing", msgDescMiss = "textIfDescriptionIsMissing", }

---Global table with internal configuration options, ---filled from module and template arguments local Config = { ---Boolean, whether to display the raw `` `` table even if	---``Data.showRawTempldataTable`` is true alwaysDisplayRawTempldataTable = false,

---Boolean, whether not to display the template description above the parameter table hideDescription = true,

---String, error message to display when the template description is not set textIfDescriptionIsMissing = false,

---String, name of the maintenance category for erroneous module calls maintenanceCateName = false,

---String, CSS styling for the parameter table stylesForParamstable = false,

---String, CSS styling for a `` `` around the parameter table stylesForParamstableWrapper = false,

---String, pattern that identifies a page to be a recognized subpage patternForMatchingSubpage = false,

---String, pattern for ``string.format`` from which the subpage title is ---created; e.g. "%s/TemplateData" to create "Foo/TemplateData" from "Foo" patternForCreatingSubpage = false,

---String, CSS class for the table of contents, ---intended to be used for suppressing the numbering classForToc = false }

---Global table with various pieces of information ---set and accessed throughout the process local Data = { ---``mw.html`` table, wrapper element around the entire output: `` `` outputWrapper = false,

---Table, initial TemplateData JSON object, obtained from ---decoding the TemplateData JSON code in ``TemplateData.getPlainJSON`` templdataJsonTurnedLua = false,

---Table, stores all parameter inheritances in the format ``{["child"]="parent"}``, ---filled in ``processTempldataJson`` and used in ``applyInheritance`` heirs = false,

---Boolean, whether to hide the entire output hideEntireOutput = false,

---Boolean, whether the main template description is missing, ---is set in ``makeTemplateOrParamDescription`` templateDescriptionIsMissing = false,

---Boolean, whether old syntax was encountered in at least one parameter's type field ---(old syntax = one of the three deprecated type values "string/line",	---"string/wiki-page-name", or "string/wiki-user-name"), ---is set in ``makeParamRow`` when making the cell for the parameter type oldSyntaxInAnyParameterType = false,

---Boolean, whether to display a table of contents, used in ``makeEntireHtmlOutput`` showToc = false,

---Table, sorted array of parameter names, set in ``getParameterOrder`` ---either from the "paramOrder" array from JSON or from the params' ---order in the TemplateData JSON code order = false,

---Table, the Lua representation of the "params" object from JSON, ---contains all information about all parameters params = false,

---String, concatenation of all error messages that are created in various functions stringWithAllErrors = false,

---String, language code of the target output language slang = false,

---String, TemplateData JSON code generated from the processed input ---(e.g. stripped of markup), is used to make the invisible `` `` tag, ---is set in ``processJsonAndMakeOutputFromIt`` templdataJsonPlain = false,

---Boolean, whether to fill ``Data.templdataJsonPlain`` in ---``processJsonAndMakeOutputFromIt``, i.e. make effective `` `` tags dontMakeRawTempldataTable = false,

---Boolean, whether to display the raw TemplateData table (used in	---``processJsonAndMakeOutputFromIt``) showRawTempldataTable = false,

---String, unprocessed TemplateData JSON code (comments *are* removed, though) ---that was found on the page templdataJson = false,

---String, contains `` `` wikitext; either the one found ---on the page (set in ``main``) or the one created from the input ---(set in ``processJsonAndMakeOutputFromIt``) templdataWikitext = false,

---Table, the Lua representation of the entire processed ---TemplateData JSON object, filled in ``processTempldataJson`` ---from ``Data.templdataJsonTurnedLua`` tree = false,

---Table, similar to ``Data.tree`` (but does not contain any	---parameter information), used for creating a functional ---`` `` tag treeForExport = false,

---Table, the ``mw.title`` object of the current page ---(or the subpage defined in Config, if that one has to be searched for TemplateData code) pageTitleObject = false }

---Pattern for finding a parameter object in the TemplateData JSON code, ---the "%s" in the middle that is not "%%s" will be replaced by ---the parameter name, in ``findPositionOfParameterObjectInJson`` local JsonParameterPattern = "[{,]%%s*(['\"])%s%%1%%s*:%%s*%%{"

---Table with all valid parameter types, as per https://www.mediawiki.org/wiki/Extension:TemplateData#parameter_types local ValidParameterTypes = { ["boolean"] = true, ["content"] = true, ["date"] = true, ["line"] = true, ["number"] = true, ["string"] = true, ["unknown"] = true, ["url"] = true, ["wiki-file-name"] = true, ["wiki-page-name"] = true, ["wiki-template-name"] = true, ["wiki-user-name"] = true, ["unbalanced-wikitext"] = true,

-- the following three are old syntax ["string/line"] = "line", ["string/wiki-page-name"] = "wiki-page-name", ["string/wiki-user-name"] = "wiki-user-name" }

---Table that contains all expected keys in the ``params`` or root JSON objects ---of the TemplateData JSON code; the values in this table are "content flags" ---that are needed by ``processTempldataJson`` local ValidJsonKeys = { params = { -- all valid JSON keys in the "params" object ["aliases"] = "table", ["autovalue"] = "string", ["default"] = "string table I18N nowiki", ["deprecated"] = "boolean string", ["description"] = "string table I18N", ["example"] = "string table I18N nowiki", ["label"] = "string table I18N", ["inherits"] = "string", ["required"] = "boolean", ["suggested"] = "boolean", ["type"] = "string" },	root = { -- all valid JSON keys in the root object ["description"] = "string table I18N", ["format"] = "string", ["maps"] = "table", ["params"] = "table", ["paramOrder"] = "table", ["sets"] = "table" } }

- -- Utility functions

---Append the ``errorMsg`` string to the global ---error string, ``Data.stringWithAllErrors``. ---@param errorMsg string The error message to append local function Fault(errorMsg) if Data.stringWithAllErrors then Data.stringWithAllErrors = string.format("%s *** %s", Data.stringWithAllErrors, errorMsg) else Data.stringWithAllErrors = errorMsg end end -- Fault

---Reduce all consecutive spaces and newlines to a single space, ---a bit like HTML does. ---@param inputText string ---@return string local function collapseWhitespace(inputText) return inputText:gsub("%s*\n%s*", " "):gsub("%s%s+", " ") end -- collapseWhitespace

- -- Regular functions

---Find the position of the ``parameterName`` in the ``Data.templdataJson``. ---Be intelligent about it, i.e. search for a JSON object that has a key that ---is recognized to be associated with a parameter object. ---@param parameterName string The name of the parameter to search for ---@param init number The position to start the search at, defaults to 1 ---@return number pos The position of the parameter local function findPositionOfParameterObjectInJson(parameterName, init) -- JsonParameterPattern has the regex for finding the parameter object, -- but it is missing the parameter name, so add it, but it -- must be prepared first, i.e. special regex characters in	-- the parameter name must be escaped: local formatStringForPattern = parameterName :gsub("%%", "%%%%") -- escape the escape character :gsub("([%-.+*?^$%[%]])", "%%%1") -- escape other special chars local parameterPattern = string.format(JsonParameterPattern, formatStringForPattern)

-- find the position of the parameter name in the JSON local startIndex, endIndex = string.find(Data.templdataJson, parameterPattern, init)

local result

-- check if there is a valid parameter object at the position of the parameter name -- that we found, and if not, retry in the text that follows it	while startIndex and not result do		-- everything after the parameter name local remainingText = string.sub(Data.templdataJson, endIndex + 1)

-- the first key of the parameter object local slice = remainingText:match("^%s*\"([^\"]+)\"s*:") or remainingText:match("^%s*'([^']+)'%s*:")

if (slice and ValidJsonKeys.params[slice]) or remainingText:match("^%s*%}") then -- the first key of the parameter object is a recognized key -- or the parameter object is empty result = endIndex else -- there is no valid parameter object at the position of the parameter name -- that we found, so retry in the text that follows it			startIndex, endIndex = string.find(Data.templdataJson, parameterPattern, endIndex) end end

return result end -- findPositionOfParameterObjectInJson

---Return a MediaWiki system message whose name begins with ``templatedata-``, ---localized text for the current language. ---@param msgName string The name of the system message after ``templatedata-`` ---@return any local function getLocalizedText(msgName) return mw.message.new("templatedata-" .. msgName) :inLanguage(Data.slang) -- translate to target language :plain -- transform to wikitext end -- getLocalizedText

---Attempt to return the Boolean value of an input. ---@param toCheck string|boolean|nil The input to retrieve the Boolean value of ---@return boolean result True if ``toCheck`` is not empty and not "0", or a Boolean; false otherwise local function checkBool(toCheck) local inputType = type(toCheck) local result if inputType == "string" then result = trim(toCheck) result = (result ~= "" and result ~= "0") elseif inputType == "boolean" then -- input already is boolean, simply return it		result = toCheck else -- input is neither string nor boolean result = false end return result end -- checkBool

---Turn the error messages stored in the global ``Data.stringWithAllErrors`` ---into an "error" `` `` and return it, add the error category if necessary. ---Also add the error messages to the error text that will be displayed above the preview. ---@return string local function getAllErrorMessages local errorStringToReturn

if Data.stringWithAllErrors then -- all error messages wrapped in an "error"-classed local errorElement = mw.html.create("span") :addClass("error") :wikitext(Data.stringWithAllErrors) errorStringToReturn = tostring(errorElement)

-- display the error messages above the preview mw.addWarning("TemplateData " .. Data.stringWithAllErrors)

-- add error category if Config.maintenanceCateName then errorStringToReturn = errorStringToReturn .. ""		end end

return errorStringToReturn or "" end -- getAllErrorMessages

---Reduce runs of spaces, including newlines, to a single space, so the ---whole string is on one line. Leave `` `` blocks alone, but ---remove the tags themselves. Manually expand ````s. ---@param inputText string Text to process ---@return string local function handleNoexportAndWhitespaceAndP(inputText) local result

-- handle noexport if inputText:find(" ", 1, true) then local i = 1 local startIndex, endIndex = inputText:find(" ", i, true) result = ""

-- handle nested tags while startIndex do			if startIndex > 1 then result = result .. collapseWhitespace(inputText:sub(i, startIndex - 1)) end i = endIndex + 1 startIndex, endIndex = inputText:find(" ", i, true) if startIndex then result = result .. inputText:sub(i, startIndex - 1) i = endIndex + 1 startIndex, endIndex = inputText:find(" ", i, true) else Fault("missing closing tag ") end end result = result .. inputText:sub(i)

else result = collapseWhitespace(inputText) end

-- handle result = result:gsub("%{%{p%|(.-)%}%}", " ")

return result end -- handleNoexportAndWhitespaceAndP

---Extract the value of the key that matches the wiki language (``Data.slang``) ---from the ``langTable``. For instance, if the wiki language is "en" and the ---``langTable`` is ``{["pl"]="foo", ["en"]="bar"}``, return "bar". --- ---Make several attempts to find the wiki language: Exact match, partial match, ---match of fallbacks. Use any of the languages if neither of the attempts is ---successful. ---@param langTable table The table from which to extract; keys should be valid ---language codes and values should be the localized texts of the respective language ---@return string|nil text The localized text in the wiki language ---@return table|nil restTable The ``langTable`` without the key that has ``text`` as value local function extractWikilangText(langTable) -- normalize the ``langTable``: remove languages for which -- the value is not a string or is an empty string; -- also, while at it, count the languages local langTableNormalized = {} local langCount = 0 for langCode, localizedText in pairs(langTable) do		if type(localizedText) == "string" then localizedText = trim(localizedText) if localizedText ~= "" then langTableNormalized[langCode] = localizedText langCount = langCount + 1 end end end

if langCount == 0 then -- there are no languages with valid strings in the langTable return nil, nil end

-- the value in the ``langTableNormalized`` at the key that -- equals the wiki language (i.e. the text that we are searching for) -- or, if there is only one language in the table, that language local textInWikilangOrInOnlylang

-- the ``langTableNormalized`` without the ``textInWikilangOrInOnlylang`` local restTable

for langCode, localizedText in pairs(langTableNormalized) do		if localizedText then if langCount == 1 then -- there is only one language in the langTableNormalized, -- so take its value textInWikilangOrInOnlylang = localizedText elseif langCode:lower == Data.slang then -- there is more than one language and we have reached -- the wiki language textInWikilangOrInOnlylang = localizedText -- remove the wiki language from the table langTableNormalized[langCode] = nil -- store the remaining languages restTable = langTableNormalized break end end end

if not textInWikilangOrInOnlylang then -- the langTableNormalized contains more than one language, -- but not the wiki language

-- hence, search again for the wiki language, but this time, -- look for keys that begin with the wiki language code plus -- a hyphen (e.g. match "zh-Hans" if wiki lang is "zh") -- (but "zh-Foo bar" also, for that matter) local langPattern = "^" .. Data.slang .. "%-"		for langCode, localizedText in pairs(langTableNormalized) do			if localizedText and langCode:lower:match(langPattern) then textInWikilangOrInOnlylang = localizedText langTableNormalized[langCode] = nil restTable = langTableNormalized break end end end

if not textInWikilangOrInOnlylang then -- still haven't found the wiki language, so search for -- fallbacks of the wiki language local others = mw.language.getFallbacksFor(Data.slang) table.insert(others, "en") -- always add EN as the last fallback for i = 1, #others do			local fallbackLang = others[i] if langTableNormalized[fallbackLang] then textInWikilangOrInOnlylang = langTableNormalized[fallbackLang] langTableNormalized[fallbackLang] = nil restTable = langTableNormalized break end end end

if not textInWikilangOrInOnlylang then -- searching for fallbacks still didn't yield any results, -- so now we can only resort to picking any language for langCode, localizedText in pairs(langTableNormalized) do			if localizedText then textInWikilangOrInOnlylang = localizedText langTableNormalized[langCode] = nil restTable = langTableNormalized break end end end

if restTable then -- report invalid language tags for langCode, localizedText in pairs(restTable) do			if localizedText then -- match any 2- to 3-ASCII-letter string, optionally appended -- with a hyphen and more ASCII letters, and optionally surrounded -- by spaces local baseCode = langCode:match("^%s*(%a%a%a?)%s*$") or langCode:match("^%s*(%a%a%a?)%-%a*%s*$") if not baseCode or not mw.language.isKnownLanguageTag(baseCode) then Fault(string.format("Invalid ", langCode)) end end end end

return textInWikilangOrInOnlylang, restTable end -- extractWikilangText

---For each of the parameters defined in ``Data.heirs``, inherit ---the parameter information and update the parameter's entries in ---``Data.params`` and ``Data.tree.params``, including setting the ---``inherits`` object to nil. Also remove the parameter from ---``Data.heirs`` if inheriting was performed successfully. local function applyInheritance -- count number of parameters in Data.heirs local paramCount = 0 for _ in pairs(Data.heirs) do		paramCount = paramCount + 1 end

if paramCount == 0 then -- Data.heirs is empty, nothing to process return end

local dataParams = Data.params local dataTreeParams = Data.tree.params

local paramsToHandle = paramCount

-- this following slightly awkward double loop facilitates -- easily avoiding circular inheritances; its functionality -- is basically: "for each param in Data.heirs, perform the	-- inheritance" for _ = 1, paramCount do		for paramChild, paramParent in pairs(Data.heirs) do			if paramParent and not Data.heirs[paramParent] then paramsToHandle = paramsToHandle - 1

-- do not handle this child again Data.heirs[paramChild] = nil dataTreeParams[paramChild].inherits = nil

-- inherit paraminfo (label, type, etc.) from the parent -- to the child: first get all the paraminfo from parent, -- then add the paraminfo from the child to that and -- overwrite data where necessary

local newParaminfoForChild = {}

-- copy paraminfo from parent to temporary table for k, v in pairs(dataParams[paramParent]) do					newParaminfoForChild[k] = v				end

-- add paraminfo from child to temporary table if dataParams[paramChild] then for k, v in pairs(dataParams[paramChild]) do						if type(v) ~= "nil" then newParaminfoForChild[k] = v						end end end

-- the temporary table newParaminfoForChild now contains -- all of the child's paraminfo plus the paraminfo -- inherited from the parent, so apply it				dataParams[paramChild] = newParaminfoForChild

-- repeat the process for Data.tree.params now -- (the first time above was for Data.params)

newParaminfoForChild = {} for k, v in pairs(dataTreeParams[paramParent]) do					newParaminfoForChild[k] = v				end for k, v in pairs(dataTreeParams[paramChild]) do					if type(v) ~= "nil" then newParaminfoForChild[k] = v					end end dataTreeParams[paramChild] = newParaminfoForChild end end end

if paramsToHandle > 0 then local errorString for paramChild, paramParent in pairs(Data.heirs) do			if paramParent then if errorString then errorString = errorString .. " &#124; " .. paramChild else errorString = "Circular inherits: " .. paramChild end end end Fault(errorString) end end -- applyInheritance

---Make a description string for the entire template or for a specific parameter, ---based on the information passed in the input ``tableWithDescription``. That input table ---needs to have a ``description`` key whose value is either the description string directly, ---or a table with two elements where the first one is a description string and the second ---one an array of description details. --- ---Use the global ``Config.textIfDescriptionIsMissing`` here and set the global ---``Data.templateDescriptionIsMissing`` here. ---@param tableWithDescription table Table with a ``description`` key ---@param shouldFailIfNoDescription boolean Whether to fail if no description can be gotten, only has an effect if the global var is set ---@return table html The HTML of the description string local function makeTemplateOrParamDescription(tableWithDescription, shouldFailIfNoDescription) local descriptionOutput = mw.html.create("div") local outputAddition -- will be appended to the output

if tableWithDescription and tableWithDescription.description then if type(tableWithDescription.description) == "string" then -- description is a simple string, just display it			descriptionOutput:wikitext(tableWithDescription.description) else -- description is not a string, but (likely) a table, so			-- display its first element as description and display its -- second element as a list descriptionOutput:wikitext(tableWithDescription.description[1])

outputAddition = mw.html.create("ul") if not Config.alwaysDisplayRawTempldataTable then -- hide list of description details outputAddition:addClass("templatedata-maintain") :css("display", "none") end for k, v in pairs(tableWithDescription.description[2]) do				outputAddition:node(mw.html.create("li")					:node(mw.html.create("code"):wikitext(k))					:node(mw.html.create("br"))					:wikitext(handleNoexportAndWhitespaceAndP(v))) end end

elseif Config.textIfDescriptionIsMissing and shouldFailIfNoDescription then descriptionOutput:addClass("error") :wikitext(Config.textIfDescriptionIsMissing) Data.templateDescriptionIsMissing = true

else -- either ``tableWithDescription`` is nil or it doesn't have a ``description`` key, -- and either there is no text for missing descriptions defined or we should fail silently, -- so don't output anything return end

if outputAddition then -- combine outputAddition and descriptionOutput in a div return mw.html.create("div") :node(descriptionOutput) :node(outputAddition) end return descriptionOutput end -- makeTemplateOrParamDescription

---Fill ``Data.order`` with the names of all parameters (based on ``Data.tree.params``), ---sorted in a way that represents their order in the TemplateData JSON code. local function sortParametersByTheirOrderInJson local paramCount = 0 local nameOfFirstParam -- only needed if there is only one parameter

-- iterate over all parameters in an unspecified order -- to find out if there are 0, 1, or >= 2 parameters for parameterName, _ in pairs(Data.tree.params) do		if paramCount == 0 then -- we are at the very first parameter Data.order = {} paramCount = 1 nameOfFirstParam = parameterName else -- we are at the second parameter, this is all -- that we need to know for now, so stop looping paramCount = 2 break end end

if paramCount < 2 then -- there is no or only one parameter, so there is no		-- order to determine – simply add the param to the array if nameOfFirstParam then table.insert(Data.order, nameOfFirstParam) end return end

-- there are at least 2 parameters, so continue

-- for each parameter, get its position in the JSON and -- attach its name to that position, then later sort the -- positions and restore the names from that; -- with this method, it doesn't matter that pairs yields -- an unspecified order

local parameterPositions = {} -- array of parameter positions local parameterPositionToName = {} -- table that stores the parameter name for each position

-- iterate over all parameters in an unspecified order for parameterName, _ in pairs(Data.tree.params) do

-- determine the position of this parameter local parameterPosition = findPositionOfParameterObjectInJson(parameterName, 1)

if parameterPosition then -- save the position of this parameter to the array of positions table.insert(parameterPositions, parameterPosition)

-- attach the name of this parameter to its position parameterPositionToName[parameterPosition] = parameterName

-- try to find the parameter a second time if findPositionOfParameterObjectInJson(parameterName, parameterPosition) then Fault(string.format("Parameter '%s' detected twice", parameterName)) end else Fault(string.format("Parameter '%s' not detected", parameterName)) end

end

-- iterate over the table that stored the parameter names with their -- positions in the numerical order of positions and add the parameter -- names to Data.order table.sort(parameterPositions) for paramPosition = 1, #parameterPositions do		-- turn current position into name local parameterName = parameterPositionToName[parameterPositions[paramPosition]] table.insert(Data.order, parameterName) end end -- sortParametersByTheirOrderInJson

---Fill the global ``Data.order`` array with the parameter names, ---ordered in the correct way. If ``Data.tree.paramOrder`` is defined, ---i.e. if there is a ``paramOrder`` array in the TemplateData JSON code, ---then use that, otherwise use the order in which the parameters are listed ---in the TemplateData JSON code. local function getParameterOrder if not Data.templdataJson then return end

if Data.tree["paramOrder"] then -- there is already an explicit order defined, so simply copy it		Data.order = {} for i = 1, #Data.tree["paramOrder"] do			table.insert(Data.order, Data.tree["paramOrder"][i]) end else -- there is no order explicitly defined, so get it from how the -- parameters are defined in the TemplateData JSON code sortParametersByTheirOrderInJson end end -- getParameterOrder

---If the ``key`` is a number, set the ``data-sort-value`` attribute of ---the ``cell`` to a zero-padded, five-character string representation of ---the number. ---@param cell table The cell whose attribute is to be adjusted ---@param key string The string which is a number or not ---@return table cell The cell with the adjusted ``data-sort-value`` attribute local function adjustSortvalueForNumericalKey(cell, key) if key:match("^%d+$") then -- key is a number, so set sort-value to a zero-padded, -- five-character string representation of the number cell:attr("data-sort-value", string.format("%05d", tonumber(key))) end return cell end -- adjustSortvalueForNumericalKey

---Make a row for the parameter outputtable with information about one parameter. ---Use the parameter's entry in the global ``Data.tree`` for that. ---@param parameterName string The name of the parameter for which to make the row ---@return table paramRow The `` `` element with the information local function makeParamRow(parameterName) -- whether this row should not be marked with a red border local legal = true

local fine = function(paramName) if paramName == trim(paramName) and paramName ~= "" then return not paramName:find("%|=\n") and not paramName:find("%s%s") end return false end

-- get data about this parameter local paraminfo = Data.tree.params[parameterName]

-- turn empty strings in the paraminfo into falses for k, v in pairs(paraminfo) do		if v == "" then paraminfo[k] = false end end

local Modes = { -- enum ["Required"] = 1, ["Suggested"] = 2, ["Optional"] = 3, ["Deprecated"] = 4 }	-- inverse of the enum, so that e.g. statusNames[1] == "required" local statusNames = {} for k, _ in pairs(Modes) do		table.insert(statusNames, k:lower) end

-- prepare status (needed for border-left on first (label) table cell) local mode if paraminfo.required then mode = Modes.Required if paraminfo.deprecated then Fault(string.format("Required deprecated ", parameterName)) legal = false end elseif paraminfo.deprecated then mode = Modes.Deprecated elseif paraminfo.suggested then mode = Modes.Suggested else mode = Modes.Optional end local status = statusNames[mode]

-- make cell 1: label local paramLabelCell = mw.html.create("td") local sortkey = paraminfo.label or parameterName paramLabelCell = adjustSortvalueForNumericalKey(paramLabelCell, sortkey) paramLabelCell:wikitext(sortkey) -- label styling, depending on parameter mode (deprecated/required/...) paramLabelCell:addClass("templatedata-doc-param") paramLabelCell:addClass("param-" .. status)

-- make cell 2: name and aliases local codeElem = mw.html.create("code") codeElem:wikitext(parameterName) if not fine(parameterName) then codeElem:addClass("error") Fault(string.format("Bad ID params. ", parameterName)) legal = false paramLabelCell:attr("data-sort-value", " " .. sortkey) end

local paramNameCell = mw.html.create("td"):node(codeElem) paramNameCell = adjustSortvalueForNumericalKey(paramNameCell, parameterName)

-- aliases if type(paraminfo.aliases) == "table" then local errorWithAnyAlias = false

-- iterate over all aliases, add them to the cell with the parameter name for _, alias in pairs(paraminfo.aliases) do			paramNameCell:tag("br")

if type(alias) == "string" then if fine(alias) then paramNameCell:node(mw.html.create("code"):wikitext(trim(alias))) else errorWithAnyAlias = true paramNameCell:node(mw.html.create("span")						:addClass("error")						:css("font-style", "italic")						:wikitext("string")					) end else errorWithAnyAlias = true paramNameCell:node(mw.html.create("code")					:addClass("error")					:wikitext(type(alias))				) end end

if errorWithAnyAlias then local invalidvalue = string.format("params. .aliases", parameterName) Fault(getLocalizedText("invalid-value"):gsub("$1", invalidvalue)) legal = false end end paramNameCell:css("font-size", "92%"):css("white-space", "nowrap")

-- make cell 3: description local descriptionCell = mw.html.create("td") local descriptionText = makeTemplateOrParamDescription(paraminfo) if descriptionText then descriptionCell:node(descriptionText) end

-- details (below description): default, example, auto if paraminfo.default or paraminfo.example or paraminfo.autovalue then local details = { "default", "example", "autovalue" } local allDetails = mw.html.create("dl"):cssText("font-size: small;")

-- iterate over the three types of details for i = 1, #details do			local thisDetailType = details[i] -- current type: default, example, or autovalue local detaildata = paraminfo[thisDetailType] if detaildata then -- label for the detail (e.g. "Auto value: ") local thisDetailLabel = mw.html.create("dt"):wikitext(getLocalizedText("doc-param-" .. thisDetailType) .. ": ") if (string.len(detaildata) < 80) then thisDetailLabel:cssText("float: left; margin-right: 1.6em; font-weight: normal;") end allDetails:node(thisDetailLabel)

-- content of the detail local thisDetailContent = mw.html.create("dd") if paraminfo.type == "boolean" then -- special formatting for parameters with Boolean values if detaildata == "0" then thisDetailContent:wikitext("False ") elseif detaildata == "1" then thisDetailContent:wikitext("True ") else thisDetailContent:wikitext(" ") end else thisDetailContent:wikitext(" ") end allDetails:node(thisDetailContent) end end descriptionCell:node(allDetails) end

-- make cell 4: type local typeCell = mw.html.create("td") if paraminfo.type then

-- Convert the parameter type to modern syntax if necessary. If conversion -- was necessary, then ``paramTypeInModernSyntax`` will hold the name of -- the parameter in modern syntax. -- Otherwise, it will be ``true`` if the parameter type is known and valid, and ``nil`` if not. local paramTypeInModernSyntax = ValidParameterTypes[paraminfo.type]

if paramTypeInModernSyntax then if type(paramTypeInModernSyntax) == "string" then -- the parameter type is in old syntax, so update it in the data source Data.params[parameterName].type = paramTypeInModernSyntax typeCell:wikitext(getLocalizedText("doc-param-type-" .. paramTypeInModernSyntax)) :tag("br") typeCell:node(mw.html.create("span")					:addClass("error")					:wikitext(paraminfo.type)) Data.oldSyntaxInAnyParameterType = true else -- the parameter type is in modern syntax, nothing to change typeCell:wikitext(getLocalizedText("doc-param-type-" .. paraminfo.type)) end else -- the parameter type is not defined in the list of valid parameter types Data.params[parameterName].type = "unknown" typeCell:addClass("error") :wikitext("INVALID") Fault(getLocalizedText("invalid-value"):gsub("$1", string.format("params. .type", parameterName))) legal = false end else typeCell:wikitext(getLocalizedText("doc-param-type-unknown")) end

-- make cell 5: status local statusText = mw.html.create("span") :wikitext(getLocalizedText("doc-param-status-" .. status)) if mode == Modes.Required or mode == Modes.Deprecated then -- emphasize the status for required and deprecated params statusText:css("font-weight", "bold") -- add deprecation details, if provided if type(paraminfo.deprecated) == "string" then statusText:tag("br") statusText:wikitext(paraminfo.deprecated) end end local statusCell = mw.html.create("td") -- sort by status order, not by status name :attr("data-sort-value", tostring(mode)) :node(statusText)

-- combine the five cells into a table row local paramRow = mw.html.create("tr") :attr("id", mw.uri.anchorEncode("templatedata:" .. parameterName)) -- for linking :node(paramLabelCell) :node(paramNameCell) :node(descriptionCell) :node(typeCell) :node(statusCell) :newline if not legal then paramRow:css("border", "#FF0000 3px solid") end

return paramRow end -- makeParamRow

---Create the table that displays information about the parameters ---from ``Data.tree`` and ``Data.tree.params``. Include header and styling. ---@return table|nil html The table object (or div if a wrapper is defined) local function makeParamsTable if not Data.tree or not Data.tree.params then -- there is nothing to display return mw.html.create("div"):wikitext("ERROR! Empty Data.tree or Data.tree.params") end

local paramsTable = mw.html.create("table") :addClass("terraria") :addClass("lined") :addClass("templatedata-doc")

-- make header local headerRow = mw.html.create("tr") :node(mw.html.create("th")			:attr("colspan", "2")			:wikitext(getLocalizedText("doc-param-name"))) :node(mw.html.create("th")			:wikitext(getLocalizedText("doc-param-desc"))) :node(mw.html.create("th")			:wikitext(getLocalizedText("doc-param-type"))) :node(mw.html.create("th")			:wikitext(getLocalizedText("doc-param-status")))

paramsTable:newline :node(headerRow) :newline

-- fill Data.order getParameterOrder

if Data.order then -- make table sortable if there are multiple parameters if #Data.order > 1 then paramsTable:addClass("sortable") end

-- add table row for each parameter for i = 1, #Data.order do			paramsTable:node(makeParamRow(Data.order[i])) end end

-- apply custom styling if necessary and return if Config.stylesForParamstable then paramsTable:cssText(Config.stylesForParamstable) end if Config.stylesForParamstableWrapper then return mw.html.create("div") :cssText(Config.stylesForParamstableWrapper) :node(paramsTable) else return paramsTable end end -- makeParamsTable

---Turn the HTML of ``Data.outputWrapper`` into a string, ---append all error messages. ---@return string local function outputhtmlToStringAndAppendErrors local stringToReturn

if Data.outputWrapper then -- regular output, formatted table stringToReturn = tostring(Data.outputWrapper) elseif Data.templdataWikitext then -- only raw templatedata table stringToReturn = Data.templdataWikitext else stringToReturn = "" end

return stringToReturn .. getAllErrorMessages end -- outputhtmlToStringAndAppendErrors

---Find TemplateData JSON within the page source of the ``Data.pageTitleObject``. ---Search for data enclosed in `` ... `` ---and for data enclosed in ``|JSON=...|data=1``. ---@return string|nil data The data that was found, ``nil`` if none found local function findJsonInPage ---@diagnostic disable-next-line: undefined-field local pageText = Data.pageTitleObject:getContent

-- search for a templatedata opening tag ("opener") local openerStartIndex, openerEndIndex = string.find(pageText, " ", 1, true) local tags = true -- the string that was found is enclosed in templatedata tags

if not openerStartIndex then -- couldn't find a templatedata opening tag, so search for template opening string ("opener") openerStartIndex, openerEndIndex = string.find(pageText, "|JSON=", 1, true) tags = false end

if openerStartIndex then -- find the closing part of the enclosure ("closer") local closerStartIndex if tags then -- the data is enclosed in templatedata tags, so search for the closing tag closerStartIndex = pageText:find(" ", openerEndIndex, true) else -- the data is in a template call, so search for the closing braces closerStartIndex = pageText:find("|data=1}}", openerEndIndex, true) end if closerStartIndex then -- extract the data, now that we have the indices of the enclosure return trim(string.sub(pageText, openerEndIndex + 1, closerStartIndex - 1)) end end return end -- findJsonInPage

---Remove most markup from the ``inputText``: ---* Turn newlines into spaces ---* Remove everything in `` `` tags ---* Replace ````s with quotes ---* Remove/replace all wiki markup ---* Escape HTML entities ---@param inputText string ---@return string result local function removeMarkup(inputText) -- (This function is called for the template description and	-- all parameter properties whose "content flags" contain "I18N",	-- and its result is put into Data.treeForExport.)

local result

if inputText then -- newlines to spaces result = inputText:gsub("\n", " ") -- remove noexport if result:find(" ", 1, true) then result = result:gsub(" (.*) ", "") end -- to quotes result = result:gsub("%{%{p%|(.-)%}%}", "&#34;%1&#34;") -- wiki markup result = plaintext._main(result) -- escape HTML entities if result:find("&", 1, true) then result = mw.text.decode(result) end end

return result end -- removeMarkup

---Make a JSON string with TemplateData JSON code, from the ---information that was previously collected (``Data.treeForExport``, ---``Data.order``, and ``Data.params`` in particular). ---@return string json The TemplateData JSON code local function makeTempldataJsonFromData local jsonString if Data.treeForExport then -- Data.treeForExport contains everything that is not -- parameter information, e.g. template description, so		-- begin with that, and replace the closing brace with a comma jsonString = mw.text.jsonEncode(Data.treeForExport):gsub("%}$", ",") else jsonString = "{" end

-- add parameter information jsonString = jsonString .. "\n\"params\":{" if Data.order then local paramsJson = {} -- table of parameter JSON strings to concatenate

-- iterate over parameters for i = 1, #Data.order do			local parameterName = Data.order[i] table.insert(paramsJson,				mw.text.jsonEncode(parameterName) .. -- param name				":" ..				mw.text.jsonEncode(Data.params[parameterName]) -- param info			) end jsonString = jsonString .. table.concat(paramsJson, ",\n")

end jsonString = jsonString .. "\n}\n}"

return jsonString end -- makeTempldataJsonFromData

---Take the ``Data.templdataJsonTurnedLua`` table and process it, ---which includes removal of wiki markup and the like. ---Put the data in the tables ``Data.tree`` and ``Data.treeForExport``. ---The function should be called once with a nil ``parameterName``, to process ---the root TemplateData JSON object, and then once for each parameter. ---@param parameterName string|nil The name of the parameter for which to perform the operations local function processTempldataJson(parameterName)

-- helper function for making error messages local f = function (a, at) local result if at then result = string.format(" ", at) else result = "root" end if a then result = string.format("%s ", result, a)		end return result end

local parent -- the table to iterate over if parameterName then -- we are iterating over one parameter object of		-- the (Lua representation of the) TemplateData JSON code parent = Data.templdataJsonTurnedLua["params"][parameterName] else -- we are iterating over the root element of		-- the (Lua representation of the) TemplateData JSON code parent = Data.templdataJsonTurnedLua end

if type(parent) ~= "table" then Fault(f .. " needs to be of  type") return end

local dataToPutIntoTree

-- tables that will be filled with the contents of the JSON object, -- will be set to Data.tree or Data.tree.params (Data.treeForExport or Data.params resp.) -- (depending on whether we are iterating over one param or root) once needed local treeTarget, treeForExportTarget

-- name of the parameter of this function call, -- nil when calling this function for the root JSON object local thisParameter

-- table of keys that are valid for the parent JSON object local validKeys -- The values of this table are "content flags" that define the content type -- of the respective values in the JSON object. -- For example, the "description" key in this table (when a ``parameterName`` is not defined) -- has the content flags "string table I18N", which means that it can be a string or a table of languages, -- which accurately represents its characteristics in the root TemplateData JSON object.

if parameterName then validKeys = ValidJsonKeys.params if type(parameterName) == "number" then thisParameter = tostring(parameterName) else thisParameter = parameterName end else validKeys = ValidJsonKeys.root end

-- iterate over the children of the parent JSON object for jsonKey, jsonValue in pairs(parent) do

-- get the "content flags" for this JSON key, e.g. "boolean" or "string table I18N" local contentFlags = validKeys[jsonKey]

if contentFlags then local valueType = type(jsonValue)

if valueType == "string" then jsonValue = trim(jsonValue) end

if contentFlags:find(valueType, 1, true) then

-- put the contents of this JSON object into Data.tree and Data.treeForExport

if contentFlags:find("I18N", 1, true) then

if valueType == "string" then dataToPutIntoTree = handleNoexportAndWhitespaceAndP(jsonValue) else local translated -- get the string for the current language from the table of defined languages jsonValue, translated = extractWikilangText(jsonValue) if jsonValue then if translated and jsonKey == "description" then dataToPutIntoTree = { [1] = handleNoexportAndWhitespaceAndP(jsonValue), [2] = translated -- the rest of the language table, minus our language }							else dataToPutIntoTree = handleNoexportAndWhitespaceAndP(jsonValue) end else dataToPutIntoTree = false end end

if jsonValue then if contentFlags:find("nowiki", 1, true) then dataToPutIntoTree = mw.text.nowiki(jsonValue) else jsonValue = removeMarkup(jsonValue) end end

else

if jsonKey == "params" and not parameterName then -- we are iterating over the root TemplateData JSON object -- and are now at the "params" child, so don't do anything with it						jsonValue = nil dataToPutIntoTree = nil

elseif jsonKey == "format" and not parameterName then -- we are iterating over the root TemplateData JSON object -- and are now at the "format" child jsonValue = mw.text.decode(jsonValue) dataToPutIntoTree = jsonValue

elseif jsonKey == "inherits" then -- we are iterating over one parameter object -- and are now at the "inherits" child dataToPutIntoTree = jsonValue

-- set the heirs of the parameter that we are iterating over -- in the global inheritance table if not Data.heirs then Data.heirs = {} end Data.heirs[thisParameter] = jsonValue

jsonValue = nil

elseif valueType == "string" then -- the current child is not any of the three above -- and is a string jsonValue = mw.text.nowiki(jsonValue) dataToPutIntoTree = jsonValue

else -- the current child is not any of the three above -- and is not a string dataToPutIntoTree = jsonValue end end

-- put data into tree if type(dataToPutIntoTree) ~= "nil" then if not treeTarget then -- this is the first child for which dataToPutIntoTree is not nil if parameterName then -- we are iterating over one parameter object, so							-- put the data into Data.tree.params if not Data.tree.params then Data.tree.params = {} end Data.tree.params[thisParameter] = {} treeTarget = Data.tree.params[thisParameter] else -- we are iterating over the root TemplateData JSON object, so							-- put the data in Data.tree Data.tree = {} treeTarget = Data.tree end end treeTarget[jsonKey] = dataToPutIntoTree dataToPutIntoTree = false end

-- put data into treeForExport if type(jsonValue) ~= "nil" then if not treeForExportTarget then -- this is the first child for which jsonValue is not nil if parameterName then -- we are iterating over one parameter object, so							-- put the data into Data.params if not Data.params then Data.params = {} end Data.params[thisParameter] = {} treeForExportTarget = Data.params[thisParameter] else -- we are iterating over the root TemplateData JSON object, so							-- put the data in Data.treeForExport Data.treeForExport = {} treeForExportTarget = Data.treeForExport end end treeForExportTarget[jsonKey] = jsonValue end else -- the type of this child of the JSON object is not among the -- "content flags" that are defined for this JSON key Fault(string.format( "Type  bad for %s", contentFlags, f(jsonKey, thisParameter) ))			end else -- the name of this child of the JSON object is not among the ``validKeys`` Fault("Unknown component " .. f(jsonKey, thisParameter)) end end end -- processTempldataJson

---Return an HTML string with the entire output: ---template description, TOC, parameters table, and format string. ---@return table div The `` `` element that wraps the output local function makeEntireHtmlOutput local div = mw.html.create("div")

-- template description if not Config.hideDescription then local templateDescription = makeTemplateOrParamDescription(Data.tree, true) if templateDescription then div:node(templateDescription) end end

-- table of contents if Data.showToc then local toc = mw.html.create("div") if Config.classForToc then toc:addClass(Config.classForToc) end toc:css("margin-top", "0.5em") :wikitext("") div:newline :node(toc) :newline end

-- main table with template parameters local paramsTable = makeParamsTable if paramsTable then if Data.showToc then local heading = mw.html.create("h2"):wikitext(getLocalizedText("doc-params")) div:node(heading) :newline end div:node(paramsTable) end

-- note about template transclusion format if Data.tree and Data.tree["format"] then local formatStyle = string.lower(Data.tree["format"]) if formatStyle ~= "inline" and formatStyle ~= "block" then -- format is neither inline nor block, so it is a custom formatting string formatStyle = "custom" end

-- display description about the formatting style local p = mw.html.create("p") :wikitext(getLocalizedText("doc-format-" .. formatStyle))

if formatStyle == "custom" then -- append the custom formatting style, wrapped in 			p:newline :node(mw.html.create("code")					:wikitext(Data.tree["format"])				) end

div:node(p) end return div end -- makeEntireHtmlOutput

---Process the TemplateData JSON code and build the HTML output from that. ---Append an invisible `` `` block, if necessary. ---When this function is done, ``Data.outputWrapper`` will hold the entire output. local function processJsonAndMakeOutputFromIt

Data.outputWrapper = mw.html.create("div"):addClass("mw-templatedata-doc-wrap") if Data.hideEntireOutput then ---@diagnostic disable-next-line: undefined-field Data.outputWrapper:cssText("display:none;") end

-- fill Data.tree/Data.treeForExport from Data.templdataJsonTurnedLua -- for the root TemplateData JSON object, i.e. things like -- template description and template transclusion format processTempldataJson

-- fill Data.tree/Data.treeForExport from Data.templdataJsonTurnedLua -- for each parameter, i.e. param name, description, etc.	if Data.treeForExport then if type(Data.templdataJsonTurnedLua["params"]) == "table" then for paramname, _ in pairs(Data.templdataJsonTurnedLua["params"]) do				processTempldataJson(paramname) end -- Data.heirs was filled in processTempldataJson -- and contains the inheritances of all parameters if Data.heirs then applyInheritance end end end

-- build HTML and append it to output ---@diagnostic disable-next-line: undefined-field Data.outputWrapper:node(makeEntireHtmlOutput)

-- build raw templatedata table and append it to output if not Data.dontMakeRawTempldataTable then

Data.templdataJsonPlain = makeTempldataJsonFromData

if not TemplateData.frame then -- there's no frame to call the parser function from return end

local templatedataTagWrapper = mw.html.create("div") Data.templdataWikitext = TemplateData.frame:callParserFunction{ name = "#tag", args = { "templatedata", Data.templdataJsonPlain } }		templatedataTagWrapper:wikitext(Data.templdataWikitext)

if Config.alwaysDisplayRawTempldataTable then -- simply display raw templatedata table, -- ignore Data.showRawTempldataTable ---@diagnostic disable-next-line: undefined-field Data.outputWrapper:node(mw.html.create("hr")) ---@diagnostic disable-next-line: undefined-field Data.outputWrapper:node(templatedataTagWrapper) else -- wrap raw templatedata table in a collapsible element, -- respect Data.showRawTempldataTable templatedataTagWrapper:addClass("mw-collapsible-content") local wrapperCollapse = mw.html.create("div") :addClass("mw-collapsible") :addClass("mw-collapsed") :css("font-size", "85%") :wikitext("Test of raw TemplateData output: ") :node(templatedataTagWrapper) if not Data.showRawTempldataTable then wrapperCollapse:css("display", "none") end ---@diagnostic disable-next-line: undefined-field Data.outputWrapper:node(wrapperCollapse) end end end -- processJsonAndMakeOutputFromIt

---Remove commented-out lines from ``Data.templdataJson``. local function removeComments string.gsub(Data.templdataJson,		"([{,\"'])(%s*\n%s*//.*\n%s*)([},\"'])",		"%1%3"	) end -- removeComments

---Fill the global ``Config`` and ``Data`` tables based on the module and template ---arguments. Retrieve the TemplateData JSON code from the page. ---@param moduleArgs table The parameters passed when invoking this module ---@param templateArgs table The parameters passed when transcluding the template that invokes this module ---@return string output The entire output string local function main(moduleArgs, templateArgs)

-- set Config parameters with input from module parameters for k, v in pairs(ConfigAliases) do		if moduleArgs[k] and moduleArgs[k] ~= "" then Config[v] = moduleArgs[k] end end Config.alwaysDisplayRawTempldataTable = checkBool(templateArgs.debug or moduleArgs.debug) Config.hideDescription = not templateArgs.description

-- language for the process: from input, or wiki default language code (disregarding user language) Data.slang = templateArgs.lang or moduleArgs.lang or mw.language.getContentLanguage:getCode

Data.hideEntireOutput = checkBool(templateArgs.hide or moduleArgs.hide) and not Config.alwaysDisplayRawTempldataTable Data.dontMakeRawTempldataTable = checkBool(templateArgs.lazy or moduleArgs.lazy) and not Config.alwaysDisplayRawTempldataTable Data.showToc = checkBool(templateArgs.TOC) Data.showRawTempldataTable = checkBool(templateArgs.showRaw)

-- get TemplateData JSON code

local source -- the TemplateData JSON code

-- for that, first check the template transclusion: $JSON and $1 if templateArgs.JSON then source = templateArgs.JSON jsonsource = "template $JSON parameter" elseif templateArgs[1] then local templateArg1 = trim(templateArgs[1]) local start = templateArg1:sub(1, 1) local charseq = mw.ustring.char(127, 39, 34, 96, 85, 78, 73, 81) --  ' " ` U N I Q		if start == "<" then			Data.templdataWikitext = templateArg1		elseif start == "{" then			source = templateArg1			jsonsource = "template $1 parameter"		elseif mw.ustring.sub(templateArg1, 1, 8) == charseq then			Data.templdataWikitext = templateArg1		end	end

-- if the template transclusion did not have any useable TemplateData, -- then check for TemplateData JSON code on the current page if not source then Data.pageTitleObject = mw.title.getCurrentTitle source = findJsonInPage jsonsource = "enclosed JSON (first try) on page " .. Data.pageTitleObject.text end

-- if the current page does not contain any TemplateData JSON code either, -- then check for TemplateData JSON code on the subpage defined in Config if not source then ---@diagnostic disable-next-line: undefined-field local currentPageNameUnprefixed = Data.pageTitleObject.text -- title of the current page without namespace ---@diagnostic disable-next-line: undefined-field local currentPageNamePrefixed = Data.pageTitleObject.prefixedText -- title of the current page with namespace

if Config.patternForMatchingSubpage and Config.patternForCreatingSubpage and not string.match(currentPageNameUnprefixed, Config.patternForMatchingSubpage) then -- only if current page is not the subpage local subpageName = string.format(Config.patternForCreatingSubpage, currentPageNamePrefixed) -- change the pageTitleObject to the one of the subpage (since findJsonInPage uses the pageTitleObject) Data.pageTitleObject = mw.title.new(subpageName)

---@diagnostic disable-next-line: undefined-field if Data.pageTitleObject.exists then source = findJsonInPage jsonsource = "enclosed JSON (second try) on page " .. Data.pageTitleObject.text end end end

-- if the TemplateData JSON code came from the subpage, -- then set Data.dontMakeRawTempldataTable to true if not Data.dontMakeRawTempldataTable and Config.patternForMatchingSubpage then if not Data.pageTitleObject then Data.pageTitleObject = mw.title.getCurrentTitle end ---@diagnostic disable-next-line: undefined-field --Data.dontMakeRawTempldataTable = string.match(Data.pageTitleObject.text, Config.patternForMatchingSubpage) end

TemplateData.getPlainJSON(source) -- process JSON and make all output

return outputhtmlToStringAndAppendErrors

end -- main

- -- TemplateData table functions

---Check the age of this implementation (``TemplateData.serial``) ---against some minimum (``assert``). ---Called when invoking the module with the ``failsafe`` function. ---@param assert any The invocation input. ---@return string|boolean result The age of this implementation if it is younger than the ``assert``, false otherwise TemplateData.failsafe = function(assert) if not assert or assert <= TemplateData.serial then return TemplateData.serial else return false end end -- TemplateData.failsafe

---Put the ``source`` TemplateData JSON code into the global ``Data.templdataJson``, ---turn it into Lua tables and process it (removing markup and making the output ---table (i.e. filling ``Data.outputWrapper`` with all HTML output)), then generate ---TemplateData JSON code from that again and return it ---@param source string The TemplateData JSON code to be processed ---@return string json The processed TemplateData JSON code TemplateData.getPlainJSON = function(source)

if type(source) ~= "string" then -- there's no usable TemplateData JSON code return end

Data.templdataJson = source -- copy to global variable

-- prepare the TemplateData JSON code removeComments

-- turn the TemplateData JSON code into a Lua table Data.templdataJsonTurnedLua = mw.text.jsonDecode(Data.templdataJson)

if Data.templdataJsonTurnedLua then processJsonAndMakeOutputFromIt

if Data.oldSyntaxInAnyParameterType then Fault('the parameter types "string/line", "string/wiki-page-name", and "string/wiki-user-name" are deprecated') end if Data.templateDescriptionIsMissing then Fault(Config.textIfDescriptionIsMissing) end

elseif not Data.templdataWikitext then Fault("fatal JSON error") end

return Data.templdataJsonPlain end -- TemplateData.getPlainJSON

---Function for simulating a ``go`` call from another module. TemplateData.test = function(moduleArgs, templateArgs) TemplateData.frame = mw.getCurrentFrame return main(moduleArgs, templateArgs) end -- TemplateData.test

- -- main return object

return {

---Main entry function for invoking the module from a template. ---Wraps ``main`` in an exception handler. ---@param frame table ---@return string go = function(frame) TemplateData.frame = frame local successful, result = pcall(main, frame.args, frame:getParent.args) if not successful then Fault("INTERNAL: " .. result) result = getAllErrorMessages end return "JSON source: " .. jsonsource .. "\n\n\n" .. result end,

---Versioning interface. ---@param frame table ---@return string result The age of the implementation of this module if the input is older; an empty string otherwise failsafe = function(frame) local since

local frameType = type(frame) if frameType == "table" then since = frame.args[1] elseif frameType == "string" then since = frame end

if since then since = trim(since) if since == "" then since = false end end

return TemplateData.failsafe(since) or "" end,

---Interface for usage by other modules. ---@return table TemplateData = function return TemplateData end

}