Module:ElementNavigation: Difference between revisions
Appearance
// via Wikitext Extension for VSCode |
// via Wikitext Extension for VSCode |
||
| Line 19: | Line 19: | ||
p.elementName = "navigation" | p.elementName = "navigation" | ||
p.defaultConfig = { | p.defaultConfig = { | ||
autoDetect = true, | autoDetect = true, | ||
patterns = { | patterns = { | ||
seriesNumber = "([^%d]+)%s+(%d+)$", | seriesNumber = "([^%d]+)%s+(%d+)$", | ||
seriesYear = "([^%d]+)%s+(%d%d%d%d)$" | seriesYear = "([^%d]+)%s+(%d%d%d%d)$" | ||
}, | }, | ||
prevField | prevField = "has_previous", | ||
nextField | nextField = "has_next", | ||
prevClass | prevClass = "element-navigation-prev", | ||
nextClass | nextClass = "element-navigation-next", | ||
prevLabel | linkPrevClass = "element-nav-prev", | ||
nextLabel | linkNextClass = "element-nav-next", | ||
rowHeight | prevLabel = "← Previous", | ||
nextLabel = "Next →", | |||
rowHeight = "40" | |||
} | } | ||
| Line 117: | Line 119: | ||
return execute(function() | return execute(function() | ||
-- | -- Merge default config with template overrides | ||
local | local rawCfg = template.config.navigation or {} | ||
local config = {} | |||
for k, v in pairs(p.defaultConfig) do | |||
config[k] = v | |||
end | |||
for k, v in pairs(rawCfg) do | |||
config[k] = v | |||
end | |||
-- Get current page name | -- Get current page name | ||
| Line 174: | Line 183: | ||
-- Get styling options | -- Get styling options | ||
local prevClass = config.prevClass | local prevClass = config.prevClass | ||
local nextClass = config.nextClass | local nextClass = config.nextClass | ||
local prevLabel = config.prevLabel | local linkPrevClass = config.linkPrevClass | ||
local nextLabel = config.nextLabel | local linkNextClass = config.linkNextClass | ||
local rowHeight = config.rowHeight | local prevLabel = config.prevLabel | ||
local nextLabel = config.nextLabel | |||
local rowHeight = config.rowHeight | |||
-- Create navigation row | -- Create navigation row | ||
| Line 189: | Line 200: | ||
if prevPage then | if prevPage then | ||
table.insert(output, string.format( | table.insert(output, string.format( | ||
'<div class=" | '<div class="%s">[[%s|%s]]</div>', | ||
prevPage, prevLabel | linkPrevClass, prevPage, prevLabel | ||
)) | )) | ||
else | else | ||
| Line 201: | Line 212: | ||
if nextPage then | if nextPage then | ||
table.insert(output, string.format( | table.insert(output, string.format( | ||
'<div class=" | '<div class="%s">[[%s|%s]]</div>', | ||
nextPage, nextLabel | linkNextClass, nextPage, nextLabel | ||
)) | )) | ||
else | else | ||
Revision as of 02:23, 27 April 2025
Documentation for this module may be created at Module:ElementNavigation/doc
--[[
* Module:ElementNavigation
* Provides navigation detection and rendering for sequential content
*
* This module detects previous/next pages based on naming patterns
* and renders navigation links. It's designed to be used as a block
* in the Blueprint template system.
*
* Features:
* - Automatic detection of previous/next pages based on patterns
* - Support for both series+number and series+year patterns
* - Caching of detection results for performance
* - Configurable field names and styling
* - Blueprint integration as a custom 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",
linkPrevClass = "element-nav-prev",
linkNextClass = "element-nav-next",
prevLabel = "← Previous",
nextLabel = "Next →",
rowHeight = "40"
}
-- Cache for navigation detection results
local navigationCache = {}
-- ========== Navigation Detection ==========
-- Detect navigation links based on patterns
-- @param pageName string The current page name
-- @param patterns table Table of patterns to use
-- @return table Navigation result with prev and next properties
function p.detectNavigation(pageName, patterns)
-- Check cache first
local cacheKey = pageName
if navigationCache[cacheKey] ~= nil then
return navigationCache[cacheKey]
end
local result = nil
-- Try Series + Number pattern: "ICANN 76"
local series, number = pageName:match(patterns.seriesNumber or "([^%d]+)%s+(%d+)$")
if series and number then
number = tonumber(number)
local prev = (number > 1) and string.format("%s %d", series, number - 1) or nil
local next = string.format("%s %d", series, number + 1)
-- Check if pages exist
local prevPage = prev and mw.title.new(prev) or nil
local nextPage = mw.title.new(next)
result = {
prev = prevPage and prevPage.exists and prev or nil,
next = nextPage and nextPage.exists and next or nil
}
end
-- If no result yet, try Series + Year pattern: "IGF 2023"
if not result then
local series, year = pageName:match(patterns.seriesYear or "([^%d]+)%s+(%d%d%d%d)$")
if series and year then
year = tonumber(year)
local prev = string.format("%s %d", series, year - 1)
local next = string.format("%s %d", series, year + 1)
-- Check if pages exist
local prevPage = mw.title.new(prev)
local nextPage = mw.title.new(next)
result = {
prev = prevPage and prevPage.exists and prev or nil,
next = nextPage and nextPage.exists and next or nil
}
end
end
-- Store in cache (including nil results)
navigationCache[cacheKey] = result
return result
end
-- ========== Blueprint Integration ==========
-- 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 config with template overrides
local rawCfg = template.config.navigation or {}
local config = {}
for k, v in pairs(p.defaultConfig) do
config[k] = v
end
for k, v in pairs(rawCfg) do
config[k] = v
end
-- Get current page name
local pageName = mw.title.getCurrentTitle().text
-- Detect navigation
local autoNavigation = nil
if config.autoDetect ~= false then
autoNavigation = p.detectNavigation(pageName, config.patterns or {})
end
-- Get field names
local prevField = config.prevField or "has_previous"
local nextField = config.nextField or "has_next"
-- Check for user-provided navigation values
local hasPrev = args[prevField]
local hasNext = args[nextField]
-- If no navigation is provided at all, return empty string
if (not hasPrev or hasPrev == "") and (not hasNext or hasNext == "") and not autoNavigation then
return ""
end
-- Determine previous and next pages
local prevPage = nil
local nextPage = nil
-- Process previous page
if hasPrev and hasPrev ~= "" then
if hasPrev ~= "yes" and hasPrev ~= "true" then
prevPage = hasPrev
elseif autoNavigation and autoNavigation.prev then
prevPage = autoNavigation.prev
end
elseif autoNavigation and autoNavigation.prev then
prevPage = autoNavigation.prev
end
-- Process next page
if hasNext and hasNext ~= "" then
if hasNext ~= "yes" and hasNext ~= "true" then
nextPage = hasNext
elseif autoNavigation and autoNavigation.next then
nextPage = autoNavigation.next
end
elseif autoNavigation and autoNavigation.next then
nextPage = autoNavigation.next
end
-- If no actual navigation links were found, return empty string
if not prevPage and not nextPage then
return ""
end
-- Get styling options
local prevClass = config.prevClass
local nextClass = config.nextClass
local linkPrevClass = config.linkPrevClass
local linkNextClass = config.linkNextClass
local prevLabel = config.prevLabel
local nextLabel = config.nextLabel
local rowHeight = config.rowHeight
-- Create navigation row
local output = {
'|-',
'| class="' .. prevClass .. '" height="' .. rowHeight .. '" valign="middle" |'
}
-- Add previous link
if prevPage then
table.insert(output, string.format(
'<div class="%s">[[%s|%s]]</div>',
linkPrevClass, prevPage, prevLabel
))
else
table.insert(output, " ")
end
-- Add next link cell
table.insert(output, '| class="' .. nextClass .. '" height="' .. rowHeight .. '" valign="middle" |')
if nextPage then
table.insert(output, string.format(
'<div class="%s">[[%s|%s]]</div>',
linkNextClass, nextPage, nextLabel
))
else
table.insert(output, " ")
end
return table.concat(output, "\n")
end)
end
end
return p