Module:ElementNavigation: Difference between revisions
Appearance
// via Wikitext Extension for VSCode Tag: Manual revert |
// via Wikitext Extension for VSCode |
||
| (4 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
--[[ | --[[ | ||
* Name: ElementNavigation | |||
* Author: Mark W. Datysgeld | |||
* Description: Element module that provides navigation detection and rendering for sequential content in Blueprint templates | |||
* Notes: Automatic detection of previous/next pages based on naming patterns; supports series+number and series+year patterns; configurable field names and styling; designed as Blueprint template system block | |||
]] | ]] | ||
| Line 30: | Line 21: | ||
prevLabel = "← Previous", | prevLabel = "← Previous", | ||
nextLabel = "Next →", | nextLabel = "Next →", | ||
classPrefix = nil, | |||
rowHeight = "40" | rowHeight = "40" | ||
} | } | ||
| Line 38: | Line 30: | ||
-- ========== Navigation Detection ========== | -- ========== Navigation Detection ========== | ||
-- Detect navigation links based on patterns | -- Detect navigation links based on a list of patterns | ||
-- @param pageName string The current page name | -- @param pageName string The current page name | ||
-- @param patterns table | -- @param patterns table A list of patterns to try | ||
-- @return table Navigation result with prev and next properties | -- @return table Navigation result with prev and next properties, or nil | ||
function p.detectNavigation(pageName, patterns) | function p.detectNavigation(pageName, patterns) | ||
local cacheKey = pageName | local cacheKey = pageName | ||
if navigationCache[cacheKey] ~= nil then | if navigationCache[cacheKey] ~= nil then | ||
return navigationCache[cacheKey] | return navigationCache[cacheKey] | ||
end | end | ||
local result = nil | local result = nil | ||
for _, pattern in ipairs(patterns) do | |||
local series, numberStr = pageName:match(pattern) | |||
if series and numberStr then | |||
local number = tonumber(numberStr) | |||
if number then | |||
local prevName = (number > 1) and string.format("%s %d", series, number - 1) or nil | |||
local nextName = string.format("%s %d", series, number + 1) | |||
local prevPage = prevName and mw.title.new(prevName) | |||
local nextPage = mw.title.new(nextName) | |||
result = { | |||
prev = (prevPage and prevPage.exists) and prevName or nil, | |||
next = (nextPage and nextPage.exists) and nextName or nil, | |||
} | |||
break -- Stop after the first successful match | |||
end | |||
end | end | ||
end | end | ||
navigationCache[cacheKey] = result | navigationCache[cacheKey] = result | ||
return result | return result | ||
| Line 93: | Line 66: | ||
-- ========== Blueprint Integration ========== | -- ========== Blueprint Integration ========== | ||
-- Helper function to merge two tables. The custom table's values override the base table's. | |||
local function mergeConfigs(base, custom) | |||
local merged = {} | |||
for k, v in pairs(base) do | |||
merged[k] = v | |||
end | |||
for k, v in pairs(custom) do | |||
merged[k] = v | |||
end | |||
return merged | |||
end | |||
-- Create a navigation block for Blueprint | -- Create a navigation block for Blueprint | ||
| Line 117: | Line 102: | ||
return execute(function() | return execute(function() | ||
-- | -- Merge default and template-specific configurations | ||
local config = template.config.navigation or {} | local config = mergeConfigs(p.defaultConfig, template.config.navigation or {}) | ||
-- | -- Automatic navigation detection | ||
local pageName = mw.title.getCurrentTitle().text | local autoNavigation | ||
if config.autoDetect then | |||
local pageName = mw.title.getCurrentTitle().text | |||
-- The patterns are now expected to be an indexed table for ipairs | |||
local patterns = {config.patterns.seriesNumber, config.patterns.seriesYear} | |||
autoNavigation = p.detectNavigation(pageName, | autoNavigation = p.detectNavigation(pageName, patterns) or {} | ||
else | |||
autoNavigation = {} | |||
end | end | ||
-- | -- Determine final previous and next page links | ||
local | local prevPage = args[config.prevField] or autoNavigation.prev | ||
local nextPage = args[config.nextField] or autoNavigation.next | |||
-- Exit if no navigation links are found | |||
local | if not prevPage and not nextPage then | ||
-- | |||
if | |||
return "" | return "" | ||
end | end | ||
-- | -- Helper to create a navigation link | ||
local function createNavLink(page, label, class) | |||
local | if not page then return '' end | ||
local linkLabel = label:find("%%s") and string.format(label, page) or label | |||
return string.format('<div class="%s">[[%s|%s]]</div>', class, page, linkLabel) | |||
end | end | ||
-- Build the navigation container | |||
-- | |||
local output = { | local output = { | ||
'|-', | '|-', | ||
'| colspan="2" height="' | string.format('| colspan="2" height="%s" valign="middle" |', config.rowHeight), | ||
'<div class="element-navigation-container">' | |||
} | } | ||
local prevLink = createNavLink(prevPage, config.prevLabel, config.prevClass) | |||
local nextLink = createNavLink(nextPage, config.nextLabel, config.nextClass) | |||
table.insert(output, prevLink) | |||
table.insert(output, nextLink) | |||
table.insert(output, '</div>') | table.insert(output, '</div>') | ||
return table.concat(output, | |||
return table.concat(output, '\n') | |||
end) | end) | ||
end | end | ||
Latest revision as of 02:58, 25 August 2025
Documentation for this module may be created at Module:ElementNavigation/doc
--[[
* Name: ElementNavigation
* Author: Mark W. Datysgeld
* Description: Element module that provides navigation detection and rendering for sequential content in Blueprint templates
* Notes: Automatic detection of previous/next pages based on naming patterns; supports series+number and series+year patterns; configurable field names and styling; designed as Blueprint template system block
]]
local p = {}
p.elementName = "navigation"
p.defaultConfig = {
autoDetect = true,
patterns = {
seriesNumber = "([^%d]+)%s+(%d+)$",
seriesYear = "([^%d]+)%s+(%d%d%d%d)$"
},
prevField = "has_previous",
nextField = "has_next",
prevClass = "element-navigation-prev",
nextClass = "element-navigation-next",
prevLabel = "← Previous",
nextLabel = "Next →",
classPrefix = nil,
rowHeight = "40"
}
-- Cache for navigation detection results
local navigationCache = {}
-- ========== Navigation Detection ==========
-- Detect navigation links based on a list of patterns
-- @param pageName string The current page name
-- @param patterns table A list of patterns to try
-- @return table Navigation result with prev and next properties, or nil
function p.detectNavigation(pageName, patterns)
local cacheKey = pageName
if navigationCache[cacheKey] ~= nil then
return navigationCache[cacheKey]
end
local result = nil
for _, pattern in ipairs(patterns) do
local series, numberStr = pageName:match(pattern)
if series and numberStr then
local number = tonumber(numberStr)
if number then
local prevName = (number > 1) and string.format("%s %d", series, number - 1) or nil
local nextName = string.format("%s %d", series, number + 1)
local prevPage = prevName and mw.title.new(prevName)
local nextPage = mw.title.new(nextName)
result = {
prev = (prevPage and prevPage.exists) and prevName or nil,
next = (nextPage and nextPage.exists) and nextName or nil,
}
break -- Stop after the first successful match
end
end
end
navigationCache[cacheKey] = result
return result
end
-- ========== Blueprint Integration ==========
-- Helper function to merge two tables. The custom table's values override the base table's.
local function mergeConfigs(base, custom)
local merged = {}
for k, v in pairs(base) do
merged[k] = v
end
for k, v in pairs(custom) do
merged[k] = v
end
return merged
end
-- Create a navigation block for Blueprint
-- @return function Block rendering function for Blueprint
function p.createBlock()
return function(template, args)
-- Get error context from template if available
local errorContext = template._errorContext
-- Protected execution if error context is available
local execute = function(func, ...)
if errorContext and errorContext.protect then
return errorContext.protect(
errorContext,
"NavigationBlock",
func,
"",
...
)
else
return func(...)
end
end
return execute(function()
-- Merge default and template-specific configurations
local config = mergeConfigs(p.defaultConfig, template.config.navigation or {})
-- Automatic navigation detection
local autoNavigation
if config.autoDetect then
local pageName = mw.title.getCurrentTitle().text
-- The patterns are now expected to be an indexed table for ipairs
local patterns = {config.patterns.seriesNumber, config.patterns.seriesYear}
autoNavigation = p.detectNavigation(pageName, patterns) or {}
else
autoNavigation = {}
end
-- Determine final previous and next page links
local prevPage = args[config.prevField] or autoNavigation.prev
local nextPage = args[config.nextField] or autoNavigation.next
-- Exit if no navigation links are found
if not prevPage and not nextPage then
return ""
end
-- Helper to create a navigation link
local function createNavLink(page, label, class)
if not page then return '' end
local linkLabel = label:find("%%s") and string.format(label, page) or label
return string.format('<div class="%s">[[%s|%s]]</div>', class, page, linkLabel)
end
-- Build the navigation container
local output = {
'|-',
string.format('| colspan="2" height="%s" valign="middle" |', config.rowHeight),
'<div class="element-navigation-container">'
}
local prevLink = createNavLink(prevPage, config.prevLabel, config.prevClass)
local nextLink = createNavLink(nextPage, config.nextLabel, config.nextClass)
table.insert(output, prevLink)
table.insert(output, nextLink)
table.insert(output, '</div>')
return table.concat(output, '\n')
end)
end
end
return p