Module:TemplateHelpers: Difference between revisions
Appearance
// via Wikitext Extension for VSCode |
// via Wikitext Extension for VSCode |
||
| Line 232: | Line 232: | ||
'<div style="display:none;">\n {{#set: Has country=' .. | '<div style="display:none;">\n {{#set: Has country=' .. | ||
countryName .. ' }}\n</div>' | countryName .. ' }}\n</div>' | ||
end | |||
end | |||
end | |||
return semanticOutput | |||
end | |||
-- Adds semantic properties for multiple regions | |||
-- This is a targeted helper that can be used within existing semantic property generation | |||
function p.addMultiRegionSemanticProperties(regionValue, semanticOutput) | |||
if not regionValue or regionValue == "" then return semanticOutput end | |||
local regions = p.splitSemicolonValues(regionValue) | |||
if #regions > 1 then | |||
for _, region in ipairs(regions) do | |||
local trimmedRegion = region:match("^%s*(.-)%s*$") | |||
if trimmedRegion and trimmedRegion ~= "" then | |||
if mw.smw then | |||
mw.smw.set({["Has region"] = trimmedRegion}) | |||
else | |||
semanticOutput = semanticOutput .. "\n" .. | |||
'<div style="display:none;">\n {{#set: Has region=' .. | |||
trimmedRegion .. ' }}\n</div>' | |||
end | |||
end | end | ||
end | end | ||
Revision as of 02:57, 6 April 2025
Documentation for this module may be created at Module:TemplateHelpers/doc
-- Module:TemplateHelpers
-- Common helper functions for template modules to promote code reuse and consistency. This module provides utilities for string processing, field handling, normalization, and standardized block rendering to be used across multiple template types.
local p = {}
-- Dependencies
local linkParser = require('Module:LinkParser')
local MultiCountryDisplay = require('Module:MultiCountryDisplay')
local dateNormalization = require('Module:DateNormalization')
local CanonicalForms = require('Module:CanonicalForms')
--------------------------------------------------------------------------------
-- String Processing Functions
--------------------------------------------------------------------------------
-- Trims leading and trailing whitespace from a string
function p.trim(s)
return (s:gsub("^%s+", ""):gsub("%s+$", ""))
end
-- Splits a semicolon-delimited string into a table of trimmed non-empty values
function p.splitSemicolonValues(value)
if not value or value == "" then return {} end
local items = {}
for item in string.gmatch(value, "[^;]+") do
local trimmed = item:match("^%s*(.-)%s*$")
if trimmed and trimmed ~= "" then
table.insert(items, trimmed)
end
end
return items
end
-- Joins a table of values with the specified delimiter
function p.joinValues(values, delimiter)
delimiter = delimiter or "; "
if not values or #values == 0 then return "" end
return table.concat(values, delimiter)
end
--------------------------------------------------------------------------------
-- Field Processing Functions
--------------------------------------------------------------------------------
-- Retrieves a field value from args using either multiple possible keys or a single key
function p.getFieldValue(args, field)
if field.keys then
for _, key in ipairs(field.keys) do
if args[key] and args[key] ~= "" then
return key, args[key]
end
end
return nil, nil
end
return field.key, (args[field.key] and args[field.key] ~= "") and args[field.key] or nil
end
-- Processes multiple values with a given processor function
function p.processMultipleValues(values, processor)
if not values or values == "" then return {} end
local items = p.splitSemicolonValues(values)
local results = {}
for _, item in ipairs(items) do
local processed = processor(item)
if processed and processed ~= "" then
table.insert(results, processed)
end
end
return results
end
--------------------------------------------------------------------------------
-- Normalization Wrappers
--------------------------------------------------------------------------------
-- Formats website URLs as either a single link or an HTML unordered list of links
function p.normalizeWebsites(value)
if not value or value == "" then return "" end
local websites = p.splitSemicolonValues(value)
if #websites > 1 then
local listItems = {}
for _, site in ipairs(websites) do
local formattedLink = string.format("[%s %s]", site, linkParser.strip(site))
table.insert(listItems, string.format("<li>%s</li>", formattedLink))
end
return string.format("<ul class=\"template-list template-list-website\" style=\"margin:0; padding-left:1em;\">%s</ul>", table.concat(listItems, ""))
elseif #websites == 1 then
return string.format("[%s %s]", websites[1], linkParser.strip(websites[1]))
end
return ""
end
-- Wrapper around MultiCountryDisplay for consistent country formatting
function p.normalizeCountries(value)
if not value or value == "" then return "" end
return MultiCountryDisplay.formatCountries(value)
end
-- Wrapper around DateNormalization for consistent date formatting
function p.normalizeDates(value)
if not value or value == "" then return "" end
return tostring(dateNormalization.formatDate(value))
end
--------------------------------------------------------------------------------
-- Block Generation Helpers
--------------------------------------------------------------------------------
-- Generates a standard title block with configurable class and text
-- Enhanced to support achievement integration with options
function p.renderTitleBlock(args, titleClass, titleText, options)
options = options or {}
titleClass = titleClass or "template-title"
-- Basic title block without achievement integration
if not options.achievementSupport then
return string.format('|-\n! colspan="2" class="%s" | %s', titleClass, titleText)
end
-- With achievement support
local achievementClass = options.achievementClass or ""
local achievementId = options.achievementId or ""
local achievementName = options.achievementName or ""
-- Only add achievement attributes if they exist
if achievementClass ~= "" and achievementId ~= "" then
return string.format(
'|-\n! colspan="2" class="%s %s" data-achievement-id="%s" data-achievement-name="%s" | %s',
titleClass, achievementClass, achievementId, achievementName, titleText
)
else
-- Clean row with no achievement data
return string.format('|-\n! colspan="2" class="%s" | %s', titleClass, titleText)
end
end
-- Renders a standard fields block based on field definitions and processors
-- Enhanced to support complete HTML blocks and custom field rendering
function p.renderFieldsBlock(args, fields, processors)
processors = processors or {}
local out = {}
for _, field in ipairs(fields) do
local key, value = p.getFieldValue(args, field)
if value then
-- Apply processor if available for this field
if key and processors[key] and type(processors[key]) == "function" then
local processedValue = processors[key](value, args)
-- Handle the case where a processor returns complete HTML
if type(processedValue) == "table" and processedValue.isCompleteHtml then
-- Add the complete HTML as is
table.insert(out, processedValue.html)
elseif processedValue ~= nil and processedValue ~= false then
-- Standard field rendering
table.insert(out, string.format("|-\n| '''%s''':\n| %s", field.label, processedValue))
end
else
-- Standard field rendering without processor
table.insert(out, string.format("|-\n| '''%s''':\n| %s", field.label, value))
end
end
end
return table.concat(out, "\n")
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
--------------------------------------------------------------------------------
-- Category Utilities
--------------------------------------------------------------------------------
-- Builds a category string from a table of category names
function p.buildCategories(categories)
if not categories or #categories == 0 then return "" end
local formatted = {}
for _, cat in ipairs(categories) do
-- Check if the category already has the [[ ]] wrapper
if not string.match(cat, "^%[%[Category:") then
table.insert(formatted, string.format("[[Category:%s]]", cat))
else
table.insert(formatted, cat)
end
end
return table.concat(formatted, "\n")
end
-- Adds categories based on a canonical mapping
function p.addMappingCategories(value, mapping)
if not value or value == "" or not mapping then return {} end
local categories = {}
local canonical = select(1, CanonicalForms.normalize(value, mapping))
if canonical then
for _, group in ipairs(mapping) do
if group.canonical == canonical and group.category then
table.insert(categories, group.category)
break
end
end
end
return categories
end
--------------------------------------------------------------------------------
-- Semantic Property Helpers
--------------------------------------------------------------------------------
-- Adds semantic properties for multiple countries
-- This is a targeted helper that can be used within existing semantic property generation
function p.addMultiCountrySemanticProperties(countryValue, semanticOutput)
if not countryValue or countryValue == "" then return semanticOutput end
local MultiCountryDisplay = require('Module:MultiCountryDisplay')
local normalizedCountries = MultiCountryDisplay.getCountriesForCategories(countryValue)
if #normalizedCountries > 1 then
for _, countryName in ipairs(normalizedCountries) do
if mw.smw then
mw.smw.set({["Has country"] = countryName})
else
semanticOutput = semanticOutput .. "\n" ..
'<div style="display:none;">\n {{#set: Has country=' ..
countryName .. ' }}\n</div>'
end
end
end
return semanticOutput
end
-- Adds semantic properties for multiple regions
-- This is a targeted helper that can be used within existing semantic property generation
function p.addMultiRegionSemanticProperties(regionValue, semanticOutput)
if not regionValue or regionValue == "" then return semanticOutput end
local regions = p.splitSemicolonValues(regionValue)
if #regions > 1 then
for _, region in ipairs(regions) do
local trimmedRegion = region:match("^%s*(.-)%s*$")
if trimmedRegion and trimmedRegion ~= "" then
if mw.smw then
mw.smw.set({["Has region"] = trimmedRegion})
else
semanticOutput = semanticOutput .. "\n" ..
'<div style="display:none;">\n {{#set: Has region=' ..
trimmedRegion .. ' }}\n</div>'
end
end
end
end
return semanticOutput
end
-- Generates semantic properties based on configuration
-- Enhanced to handle common patterns while allowing template-specific customization
function p.generateSemanticProperties(args, semanticConfig, options)
if not args or not semanticConfig then return "" end
local SemanticAnnotations = require('Module:SemanticAnnotations')
options = options or {}
-- Set options
local semanticOptions = {
transform = semanticConfig.transforms or options.transform
}
-- Set basic properties
local semanticOutput = SemanticAnnotations.setSemanticProperties(
args,
semanticConfig.properties,
semanticOptions
)
-- Handle boolean flags
if options.booleanFlags then
for property, flagName in pairs(options.booleanFlags) do
if args[flagName] then
if mw.smw then
mw.smw.set({[property] = "true"})
else
semanticOutput = semanticOutput .. "\n" ..
'<div style="display:none;">\n {{#set: ' .. property .. '=true }}\n</div>'
end
end
end
end
-- Handle multiple values
if semanticConfig.additionalProperties then
for property, sourceFields in pairs(semanticConfig.additionalProperties) do
for _, fieldName in ipairs(sourceFields) do
if args[fieldName] and args[fieldName] ~= "" then
local value = args[fieldName]
-- Apply transformation if available
if semanticConfig.transforms and semanticConfig.transforms[property] then
value = semanticConfig.transforms[property](value)
end
if mw.smw then
mw.smw.set({[property] = value})
else
semanticOutput = semanticOutput .. "\n" ..
'<div style="display:none;">\n {{#set: ' .. property .. '=' ..
value .. ' }}\n</div>'
end
end
end
end
end
-- Handle multi-value fields that need to be split
if options.multiValueFields then
for property, config in pairs(options.multiValueFields) do
local fieldName = config.field
local splitter = config.splitter or p.splitSemicolonValues
local processor = config.processor
if args[fieldName] and args[fieldName] ~= "" then
local values = splitter(args[fieldName])
for _, value in ipairs(values) do
-- Apply processor if available
if processor and type(processor) == "function" then
value = processor(value)
end
if value and value ~= "" then
if mw.smw then
mw.smw.set({[property] = value})
else
semanticOutput = semanticOutput .. "\n" ..
'<div style="display:none;">\n {{#set: ' .. property .. '=' ..
value .. ' }}\n</div>'
end
end
end
end
end
end
-- Handle custom special cases
if options.specialCaseHandler and type(options.specialCaseHandler) == "function" then
local specialCaseOutput = options.specialCaseHandler(args, semanticOutput, semanticConfig)
if specialCaseOutput then
semanticOutput = specialCaseOutput
end
end
return semanticOutput
end
--------------------------------------------------------------------------------
-- Configuration Standardization
--------------------------------------------------------------------------------
-- Creates a standardized configuration structure for template modules
-- This ensures all templates have a consistent configuration format
function p.createStandardConfig(config)
config = config or {}
-- Initialize with defaults
local standardConfig = {
meta = config.meta or {
description = "Template module configuration"
},
mappings = config.mappings or {},
fields = config.fields or {},
semantics = config.semantics or {
properties = {},
transforms = {},
additionalProperties = {}
},
constants = config.constants or {},
patterns = config.patterns or {}
}
return standardConfig
end
return p