Module:TemplateStructure: Difference between revisions
Appearance
No edit summary Tag: Reverted |
// via Wikitext Extension for VSCode |
||
| (31 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
--[[ | |||
* Name: TemplateStructure | |||
* Author: Mark W. Datysgeld | |||
* Description: Block-based template rendering module with centralized error reporting integration | |||
* Notes: Block rendering functions for templates; title blocks; divider blocks; field tables; configurable CSS classes and attributes; ARIA support; container tag support for div wrappers; error handling integration | |||
]] | |||
local ErrorHandling = require('Module:ErrorHandling') | |||
local NormalizationText = require('Module:NormalizationText') | |||
local p = {} | local p = {} | ||
-- Local trim function: removes leading and trailing whitespace. | |||
-- Now delegates to NormalizationText | |||
local function trim(s) | |||
return NormalizationText.trim(s) | |||
end | |||
--[[ | --[[ | ||
p.render(args, config) | p.render(args, config, errorContext) | ||
Parameters: | Parameters: | ||
args | args - Template parameters (passed to each block function) | ||
config - | config - Configuration options: | ||
tableClass: | tableClass: CSS class for the table (default: "template-table") | ||
tableAttrs: Additional table attributes (default: 'cellpadding="2"') | |||
tableAttrs: | blocks: Functions that generate table rows | ||
Each accepts (args, config) and returns a string | |||
blocks: | continueOnError: Continue rendering if a block fails (default: true) | ||
Each | errorContext - Optional error context from ErrorHandling.createContext() | ||
If provided, errors will be reported to this context | |||
Returns: | Returns: | ||
Wikitext markup for the complete table | |||
]] | ]] | ||
function p.render(args, config) | function p.render(args, config, errorContext) | ||
config = config or {} | config = config or {} | ||
-- ARIA support | |||
local ariaLabelledBy = config.ariaLabelledBy and string.format('aria-labelledby="%s"', config.ariaLabelledBy) or '' | |||
-- Support fullPage mode: render blocks inside a <div> wrapper when requested | |||
if config.containerTag == 'div' then | |||
local out = {} | |||
local tag = config.containerTag | |||
-- open container without default template-table class when empty | |||
local cls = config.tableClass or '' | |||
local attr = {ariaLabelledBy} | |||
if cls ~= '' then | |||
table.insert(attr, 1, string.format('class="%s"', cls)) | |||
end | |||
table.insert(out, string.format('<%s %s>', tag, table.concat(attr, ' '))) | |||
for i, block in ipairs(config.blocks or {}) do | |||
local ok, blk = pcall(block, args, config) | |||
if ok and blk and blk ~= "" then | |||
table.insert(out, trim(blk)) | |||
elseif not ok and errorContext then | |||
ErrorHandling.addError( | |||
errorContext, | |||
"TemplateStructure", | |||
"Block #" .. i .. " execution failed", | |||
tostring(blk), | |||
false | |||
) | |||
end | |||
end | |||
-- close container | |||
table.insert(out, string.format('</%s>', tag)) | |||
return table.concat(out, "\n") | |||
end | |||
local tableClass = config.tableClass or "template-table" | local tableClass = config.tableClass or "template-table" | ||
local tableAttrs = config.tableAttrs or | local tableAttrs = config.tableAttrs or 'cellpadding="2"' | ||
local blocks = config.blocks or {} | local blocks = config.blocks or {} | ||
local continueOnError = config.continueOnError | |||
if continueOnError == nil then continueOnError = true end -- Default to true | |||
-- | -- Begin the table markup | ||
local | local result = {} | ||
table.insert(result, string.format('{| class="%s" %s %s', tableClass, tableAttrs, ariaLabelledBy)) | |||
-- Process each block function | -- Process each block function in the supplied order | ||
for i, block in ipairs(blocks) do | for i, block in ipairs(blocks) do | ||
if type(block) == "function" then | if type(block) == "function" then | ||
local blockOutput = block(args, config) | -- Protect block execution to avoid template failure | ||
if blockOutput | local success, blockOutput = pcall(function() | ||
return block(args, config) | |||
end) | |||
if success then | |||
blockOutput = trim(blockOutput) | |||
if blockOutput ~= "" then | |||
table.insert(result, blockOutput) | |||
end | |||
else | |||
-- Get error message | |||
local errorMsg = tostring(blockOutput) | |||
-- Report to error context if available | |||
if errorContext then | |||
ErrorHandling.addError( | |||
errorContext, | |||
"TemplateStructure", | |||
"Block #" .. i .. " execution failed", | |||
errorMsg, | |||
false | |||
) | |||
end | |||
-- If we shouldn't continue on error, break the loop | |||
if not continueOnError then | |||
break | |||
end | |||
end | end | ||
else | else | ||
-- Report non-function blocks to error context if available | |||
if errorContext then | |||
ErrorHandling.addError( | |||
errorContext, | |||
"TemplateStructure", | |||
"Block #" .. i .. " is not a function", | |||
"Expected function, got " .. type(block), | |||
false | |||
) | |||
end | |||
end | end | ||
end | end | ||
-- Close the table | |||
table.insert(result, "|}") | |||
local finalOutput = table.concat(result, "\n") | |||
return trim(finalOutput) | |||
end | |||
-- Renders a standard title block with configurable class and text (basic version) | |||
function p.renderTitleBlock(args, titleClass, titleText, titleId) | |||
titleClass = titleClass or "template-title" | |||
-- ARIA title | |||
local idAttr = titleId and string.format('id="%s"', titleId) or '' | |||
local titleSpan = string.format('<span %s>%s</span>', idAttr, titleText) | |||
return string.format('|-\n! colspan="2" class="%s" | %s', titleClass, titleSpan) | |||
end | |||
-- | -- Renders a standard divider block with optional label | ||
return | function p.renderDividerBlock(label) | ||
if label and label ~= "" then | |||
return string.format('|-\n| colspan="2" class="template-divider" |\n|-\n| colspan="2" class="icannwiki-centered" | <span class="icannwiki-bold">%s</span>', label) | |||
else | |||
return '|-\n| colspan="2" class="template-divider" |' | |||
end | |||
end | |||
--[[ | |||
Renders a table of fields with labels and values using TemplateStructure. | |||
Parameters: | |||
fields - Array of field objects, each with: | |||
- label: The field label to display | |||
- value: The field value to display | |||
- class: Optional CSS class for the row | |||
options - Optional configuration: | |||
- tableClass: CSS class for the table (default: "template-field-table") | |||
- tableAttrs: Additional table attributes | |||
- fieldFormat: Format string for field rows (default: uses FIELD_FORMAT) | |||
- errorContext: Optional error context for error handling | |||
Returns: | |||
Wikitext markup for the field table | |||
]] | |||
function p.renderFieldTable(fields, options) | |||
-- Early return for empty fields | |||
if not fields or #fields == 0 then | |||
return "" | |||
end | |||
options = options or {} | |||
-- Define the field table rendering operation | |||
local function renderFieldTableOperation(fields, options) | |||
-- Create a config for the render function with optimized defaults | |||
local config = { | |||
tableClass = options.tableClass or "template-field-table", | |||
tableAttrs = options.tableAttrs or 'cellpadding="2"', | |||
blocks = {} | |||
} | |||
-- Pre-allocate blocks array based on field count | |||
local blocks = {} | |||
-- Get the field format from options or use a default | |||
local fieldFormat = options.fieldFormat or '|- class="template-data-row"\n| class="template-label-cell" | <span class="template-label-style">%s</span>\n| class="template-value-cell" | %s' | |||
-- Create a block function for each field with direct index assignment | |||
for i = 1, #fields do | |||
local field = fields[i] | |||
blocks[i] = function() | |||
-- Combine the field's class with the template-data-row class | |||
local fieldClass = field.class and field.class or "" | |||
-- Create the row with proper classes | |||
local row = '|- class="template-data-row' .. (fieldClass ~= "" and ' ' .. fieldClass or '') .. '"' | |||
-- Format the cells using the field format but replace the row start | |||
local cellsFormat = fieldFormat:gsub("^[^|]+", "") | |||
-- Return the complete row | |||
return row .. string.format(cellsFormat, field.label, field.value) | |||
end | |||
end | |||
-- Assign blocks to config | |||
config.blocks = blocks | |||
-- Use TemplateStructure's render function | |||
return p.render({}, config, options.errorContext) | |||
end | |||
-- Use error handling if provided | |||
if options.errorContext then | |||
return ErrorHandling.protect( | |||
options.errorContext, | |||
"renderFieldTable", | |||
renderFieldTableOperation, | |||
"", -- Empty string fallback | |||
fields, options | |||
) | |||
else | |||
-- Direct execution without error handling | |||
return renderFieldTableOperation(fields, options) | |||
end | |||
end | end | ||
return p | return p | ||
Latest revision as of 03:13, 25 August 2025
Documentation for this module may be created at Module:TemplateStructure/doc
--[[
* Name: TemplateStructure
* Author: Mark W. Datysgeld
* Description: Block-based template rendering module with centralized error reporting integration
* Notes: Block rendering functions for templates; title blocks; divider blocks; field tables; configurable CSS classes and attributes; ARIA support; container tag support for div wrappers; error handling integration
]]
local ErrorHandling = require('Module:ErrorHandling')
local NormalizationText = require('Module:NormalizationText')
local p = {}
-- Local trim function: removes leading and trailing whitespace.
-- Now delegates to NormalizationText
local function trim(s)
return NormalizationText.trim(s)
end
--[[
p.render(args, config, errorContext)
Parameters:
args - Template parameters (passed to each block function)
config - Configuration options:
tableClass: CSS class for the table (default: "template-table")
tableAttrs: Additional table attributes (default: 'cellpadding="2"')
blocks: Functions that generate table rows
Each accepts (args, config) and returns a string
continueOnError: Continue rendering if a block fails (default: true)
errorContext - Optional error context from ErrorHandling.createContext()
If provided, errors will be reported to this context
Returns:
Wikitext markup for the complete table
]]
function p.render(args, config, errorContext)
config = config or {}
-- ARIA support
local ariaLabelledBy = config.ariaLabelledBy and string.format('aria-labelledby="%s"', config.ariaLabelledBy) or ''
-- Support fullPage mode: render blocks inside a <div> wrapper when requested
if config.containerTag == 'div' then
local out = {}
local tag = config.containerTag
-- open container without default template-table class when empty
local cls = config.tableClass or ''
local attr = {ariaLabelledBy}
if cls ~= '' then
table.insert(attr, 1, string.format('class="%s"', cls))
end
table.insert(out, string.format('<%s %s>', tag, table.concat(attr, ' ')))
for i, block in ipairs(config.blocks or {}) do
local ok, blk = pcall(block, args, config)
if ok and blk and blk ~= "" then
table.insert(out, trim(blk))
elseif not ok and errorContext then
ErrorHandling.addError(
errorContext,
"TemplateStructure",
"Block #" .. i .. " execution failed",
tostring(blk),
false
)
end
end
-- close container
table.insert(out, string.format('</%s>', tag))
return table.concat(out, "\n")
end
local tableClass = config.tableClass or "template-table"
local tableAttrs = config.tableAttrs or 'cellpadding="2"'
local blocks = config.blocks or {}
local continueOnError = config.continueOnError
if continueOnError == nil then continueOnError = true end -- Default to true
-- Begin the table markup
local result = {}
table.insert(result, string.format('{| class="%s" %s %s', tableClass, tableAttrs, ariaLabelledBy))
-- Process each block function in the supplied order
for i, block in ipairs(blocks) do
if type(block) == "function" then
-- Protect block execution to avoid template failure
local success, blockOutput = pcall(function()
return block(args, config)
end)
if success then
blockOutput = trim(blockOutput)
if blockOutput ~= "" then
table.insert(result, blockOutput)
end
else
-- Get error message
local errorMsg = tostring(blockOutput)
-- Report to error context if available
if errorContext then
ErrorHandling.addError(
errorContext,
"TemplateStructure",
"Block #" .. i .. " execution failed",
errorMsg,
false
)
end
-- If we shouldn't continue on error, break the loop
if not continueOnError then
break
end
end
else
-- Report non-function blocks to error context if available
if errorContext then
ErrorHandling.addError(
errorContext,
"TemplateStructure",
"Block #" .. i .. " is not a function",
"Expected function, got " .. type(block),
false
)
end
end
end
-- Close the table
table.insert(result, "|}")
local finalOutput = table.concat(result, "\n")
return trim(finalOutput)
end
-- Renders a standard title block with configurable class and text (basic version)
function p.renderTitleBlock(args, titleClass, titleText, titleId)
titleClass = titleClass or "template-title"
-- ARIA title
local idAttr = titleId and string.format('id="%s"', titleId) or ''
local titleSpan = string.format('<span %s>%s</span>', idAttr, titleText)
return string.format('|-\n! colspan="2" class="%s" | %s', titleClass, titleSpan)
end
-- Renders a standard divider block with optional label
function p.renderDividerBlock(label)
if label and label ~= "" then
return string.format('|-\n| colspan="2" class="template-divider" |\n|-\n| colspan="2" class="icannwiki-centered" | <span class="icannwiki-bold">%s</span>', label)
else
return '|-\n| colspan="2" class="template-divider" |'
end
end
--[[
Renders a table of fields with labels and values using TemplateStructure.
Parameters:
fields - Array of field objects, each with:
- label: The field label to display
- value: The field value to display
- class: Optional CSS class for the row
options - Optional configuration:
- tableClass: CSS class for the table (default: "template-field-table")
- tableAttrs: Additional table attributes
- fieldFormat: Format string for field rows (default: uses FIELD_FORMAT)
- errorContext: Optional error context for error handling
Returns:
Wikitext markup for the field table
]]
function p.renderFieldTable(fields, options)
-- Early return for empty fields
if not fields or #fields == 0 then
return ""
end
options = options or {}
-- Define the field table rendering operation
local function renderFieldTableOperation(fields, options)
-- Create a config for the render function with optimized defaults
local config = {
tableClass = options.tableClass or "template-field-table",
tableAttrs = options.tableAttrs or 'cellpadding="2"',
blocks = {}
}
-- Pre-allocate blocks array based on field count
local blocks = {}
-- Get the field format from options or use a default
local fieldFormat = options.fieldFormat or '|- class="template-data-row"\n| class="template-label-cell" | <span class="template-label-style">%s</span>\n| class="template-value-cell" | %s'
-- Create a block function for each field with direct index assignment
for i = 1, #fields do
local field = fields[i]
blocks[i] = function()
-- Combine the field's class with the template-data-row class
local fieldClass = field.class and field.class or ""
-- Create the row with proper classes
local row = '|- class="template-data-row' .. (fieldClass ~= "" and ' ' .. fieldClass or '') .. '"'
-- Format the cells using the field format but replace the row start
local cellsFormat = fieldFormat:gsub("^[^|]+", "")
-- Return the complete row
return row .. string.format(cellsFormat, field.label, field.value)
end
end
-- Assign blocks to config
config.blocks = blocks
-- Use TemplateStructure's render function
return p.render({}, config, options.errorContext)
end
-- Use error handling if provided
if options.errorContext then
return ErrorHandling.protect(
options.errorContext,
"renderFieldTable",
renderFieldTableOperation,
"", -- Empty string fallback
fields, options
)
else
-- Direct execution without error handling
return renderFieldTableOperation(fields, options)
end
end
return p