Module:TemplateStructure: Difference between revisions

Undo revision 1359365 by MarkWD (talk)
Tag: Undo
// via Wikitext Extension for VSCode
 
(21 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.
-- Local trim function: removes leading and trailing whitespace.
-- Now delegates to NormalizationText
local function trim(s)
local function trim(s)
     return (s:gsub("^%s+", ""):gsub("%s+$", ""))
     return NormalizationText.trim(s)
end
end


--[[
--[[
     p.render(args, config)
     p.render(args, config, errorContext)
   
 
    Renders a table using a modular, block-based approach.
   
     Parameters:
     Parameters:
       args   - Table of template parameters (passed to each block function).
       args         - Template parameters (passed to each block function)
       config - Table containing configuration options:
       config       - Configuration options:
           tableClass: (string) CSS class for the table.
           tableClass: CSS class for the table (default: "template-table")
                      Default: "template-table"
           tableAttrs: Additional table attributes (default: 'cellpadding="2"')
           tableAttrs: (string) Additional attributes for the table tag.
           blocks:    Functions that generate table rows
                      Default: 'cellpadding="2"'
                       Each accepts (args, config) and returns a string
           blocks:    (array) An ordered list of functions (blocks) that generate table rows.
          continueOnError: Continue rendering if a block fails (default: true)
                       Each block function should accept (args, config) and return a string.
      errorContext - Optional error context from ErrorHandling.createContext()
                    If provided, errors will be reported to this context
      
      
     Returns:
     Returns:
       A string containing the full wikitext markup for the table.
       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 'cellpadding="2"'
     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.
     -- Begin the table markup
     local result = {}
     local result = {}
     table.insert(result, string.format('{| class="%s" %s', tableClass, tableAttrs))
     table.insert(result, string.format('{| class="%s" %s %s', tableClass, tableAttrs, ariaLabelledBy))
      
      
     -- Process each block function in the supplied order.
     -- 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
             blockOutput = trim(blockOutput)
             local success, blockOutput = pcall(function()
            if blockOutput ~= "" then
                return block(args, config)  
                table.insert(result, blockOutput)
             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
             mw.log("Warning: Block #" .. i .. " is not a function and will be skipped.")
             -- 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, "|}")
     table.insert(result, "|}")
   
     local finalOutput = table.concat(result, "\n")
     local finalOutput = table.concat(result, "\n")
     return trim(finalOutput)
     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
end


return p
return p