Jump to content

Module:TemplateStructure: Difference between revisions

No edit summary
Tag: Manual revert
// via Wikitext Extension for VSCode
 
(25 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)
   
 
    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
                      Note: Using cellpadding here is a legacy technique that ensures
          continueOnError: Continue rendering if a block fails (default: true)
                      a minimum amount of spacing in table cells, which helps maintain
      errorContext - Optional error context from ErrorHandling.createContext()
                      readability and consistent rendering across different skins and
                    If provided, errors will be reported to this context
                      browsers. It can be overridden via config if desired.
           blocks:    (array) An ordered list of functions (blocks) that generate table rows.
                       Each block function should accept (args, config) and return a string.
      
      
     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
             if blockOutput and blockOutput ~= "" then
             local success, blockOutput = pcall(function()
                table.insert(result, blockOutput)
                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
             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, "|}")
     return table.concat(result, "\n")
     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
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