Module:Format TemplateData
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Format TemplateData/doc
-- Module for displaying a table with TemplateData information of
-- a template that is much richer than the table that is displayed
-- by the <templatedata> 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 = {}
local plaintext = require("Module:Plain text")
local trim = mw.text.trim
-----------------------------------------------------------------
-- 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",
detailtrue = "paramDetailValueTrue",
detailfalse = "paramDetailValueFalse"
}
---Global table with internal configuration options,
---filled from module and template arguments
local Config = {
---Boolean, whether to display the raw ``<templatedata>`` 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 ``<div>`` around the parameter table
stylesForParamstableWrapper = false,
---String, pattern that identifies a page to be a documentation subpage
patternForMatchingSubpage = false,
---String, pattern for ``string.format()`` from which the documentation subpage
---title is created; e.g. "%s/doc" to create "Foo/doc" from "Foo"
patternForCreatingSubpage = false,
---String, CSS class for the table of contents,
---intended to be used for suppressing the numbering
classForToc = false,
---String, HTML to display when the default/example/auto value of
---a parameter is "True" (1)
paramDetailValueTrue = false,
---String, HTML to display when the default/example/auto value of
---a parameter is "False" (0)
paramDetailValueFalse = 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: ``<div class="mw-templatedata-doc-wrap">``
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 ``<templatedata>`` tag,
---is set in ``processJsonAndMakeOutputFromIt()``
templdataJsonPlain = false,
---Boolean, whether to fill ``Data.templdataJsonPlain`` in
---``processJsonAndMakeOutputFromIt()``, i.e. make effective ``<templatedata>`` 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 ``<templatedata>`` 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
---``<templatedata>`` 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 these tables 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", "n", or "no"; false otherwise
local function checkBool(toCheck)
local inputType = type(toCheck)
local result
if inputType == "string" then
result = trim(toCheck)
result = (result ~= "" and result ~= "0" and result ~= "n" and result ~= "no")
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" ``<span>`` 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 <span>
local errorElement = mw.html.create("span")
:addClass("error")
:wikitext(Data.stringWithAllErrors)
errorStringToReturn = tostring(errorElement)
-- display the error messages above the preview
mw.addWarning("'''TemplateData'''<br/>" .. Data.stringWithAllErrors)
-- add error category
if Config.maintenanceCateName then
errorStringToReturn = errorStringToReturn .. "[[Category:" .. Config.maintenanceCateName .. "]]"
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 ``<noexport>`` blocks alone, but
---remove the <noexport> tags themselves. Manually expand ``{{p}}``s.
---@param inputText string Text to process
---@return string
local function handleNoexportAndWhitespaceAndP(inputText)
local result
-- handle noexport
if inputText:find("<noexport>", 1, true) then
local i = 1
local startIndex, endIndex = inputText:find("<noexport>", 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("</noexport>", i, true)
if startIndex then
result = result .. inputText:sub(i, startIndex - 1)
i = endIndex + 1
startIndex, endIndex = inputText:find("<noexport>", i, true)
else
Fault("missing closing tag </noexport>")
end
end
result = result .. inputText:sub(i)
else
result = collapseWhitespace(inputText)
end
-- handle {{p}}
result = result:gsub("%{%{p%|(.-)%}%}", "<code>[[#templatedata:%1|$%1]]</code>")
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 <code>lang=%s</code>", 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 .. " | " .. 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 ``<tr>`` 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 statusname, statusvalue in pairs(Modes) do
table.insert(statusNames, statusvalue, statusname: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 <code>%s</code>", 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.<code>%s</code>", 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.<code>%s</code>.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
-- (only if available, otherwise fall back to standard formatting)
if detaildata == "0" then
thisDetailContent:wikitext(Config.paramDetailValueFalse or "<code>0</code>")
elseif detaildata == "1" then
thisDetailContent:wikitext(Config.paramDetailValueTrue or "<code>1</code>")
else
thisDetailContent:wikitext("<code>" .. detaildata .. "</code>")
end
else
thisDetailContent:wikitext("<code>" .. detaildata .. "</code>")
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.<code>%s</code>.type", parameterName)))
legal = false
end
else
typeCell:wikitext(getLocalizedText("doc-param-type-unknown"))
end
-- make cell 5: status
local statusText = mw.html.create("div")
: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:node(mw.html.create("div")
:css("font-size", "92%"):css("font-style", "italic")
: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:addClass("templatedata-doc-param-illegal")
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
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 ``<templatedata>...</templatedata>``
---and for data enclosed in ``|JSON=...|data=1``.
---@return string|nil data The data that was found, ``nil`` if none found
local function findJsonInPage()
local pageText = Data.pageTitleObject:getContent()
-- search for a templatedata opening tag ("opener")
local openerStartIndex, openerEndIndex = string.find(pageText, "<templatedata>", 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("</templatedata>", 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 ``<noexport>`` tags
---* Replace ``{{p}}``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("<noexport>", 1, true) then
result = result:gsub("<noexport>(.*)</noexport>", "")
end
-- {{p}} to quotes
result = result:gsub("%{%{p%|(.-)%}%}", ""%1"")
-- 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("<code>params.%s</code>", at)
else
result = "''root''"
end
if a then
result = string.format("%s<code>.%s</code>", 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 <code>object</code> 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 <code>%s</code> 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 ``<div>`` 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("__TOC__")
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 <code>
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 ``<templatedata>`` 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
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
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
Data.outputWrapper:node(mw.html.create("hr"))
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
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
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) -- <DEL> ' " ` U N I Q
if start == "<" then
Data.templdataWikitext = templateArg1
elseif start == "{" then
source = templateArg1
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()
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
-- (most likely a documentation subpage)
if not source then
local currentPageNameUnprefixed = Data.pageTitleObject.text -- title of the current page without namespace
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)
if Data.pageTitleObject.exists then
source = findJsonInPage()
end
end
end
--[[
this part seems unwanted?
if the TemplateData JSON code came from the doc subpage, then it would actually be wanted to make
a functional <templatedata> tag for the template main page, so that it gets recognized by VisualEditor.
anyway, if this part is active, particularly the second to last line, then the data used by the VE seems
to be taken from the <templatedata> tags inside the {{templatedata}} call on the doc subpage – and no
processing occurs whatsoever.
-- 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
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
---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 result
end,
---Interface for usage by other modules.
---@return table
TemplateData = function()
return TemplateData
end
}