Module:ElementNavigation: Difference between revisions
Appearance
// via Wikitext Extension for VSCode |
// via Wikitext Extension for VSCode Tag: Reverted |
||
| Line 14: | Line 14: | ||
* - Blueprint integration as a custom block | * - Blueprint integration as a custom block | ||
]] | ]] | ||
-- External dependencies | |||
local ErrorHandling = require('Module:ErrorHandling') | |||
local Blueprint = require('Module:LuaTemplateBlueprint') | |||
-- MediaWiki global for linting | |||
local mw = mw or {} -- This allows linting to work while still using the global mw in MediaWiki | |||
local p = {} | local p = {} | ||
p.elementName = "navigation" | p.elementName = "navigation" | ||
-- Helper function to add this element to a template with custom configuration | |||
-- @param template table The Blueprint template object | |||
-- @param errorContext table The error context for safe require | |||
-- @param options table Optional configuration overrides | |||
function p.addToTemplate(template, errorContext, options) | |||
options = options or {} | |||
-- Only proceed if we're properly loaded | |||
local self = errorContext and ErrorHandling.safeRequire(errorContext, 'Module:ElementNavigation', false) or p | |||
if self and self.elementName then | |||
-- Register this element with Blueprint | |||
Blueprint.registerElement(self.elementName, self) | |||
-- Add the element to the template | |||
Blueprint.addElementToTemplate(template, 'navigation') | |||
-- Set up configuration with defaults that can be overridden | |||
template.config.navigation = { | |||
autoDetect = options.autoDetect ~= nil and options.autoDetect or true, | |||
patterns = options.patterns or self.defaultConfig.patterns, | |||
prevField = options.prevField or self.defaultConfig.prevField, | |||
nextField = options.nextField or self.defaultConfig.nextField, | |||
prevLabel = options.prevLabel or "← %s", | |||
nextLabel = options.nextLabel or "%s →", | |||
prevClass = options.prevClass or self.defaultConfig.prevClass, | |||
nextClass = options.nextClass or self.defaultConfig.nextClass, | |||
rowHeight = options.rowHeight or self.defaultConfig.rowHeight | |||
} | |||
return true | |||
end | |||
return false | |||
end | |||
p.defaultConfig = { | p.defaultConfig = { | ||
autoDetect = true, | autoDetect = true, | ||
| Line 208: | Line 252: | ||
-- Use "class=" parameter in MediaWiki link syntax for proper styling | -- Use "class=" parameter in MediaWiki link syntax for proper styling | ||
table.insert(output, string.format( | table.insert(output, string.format( | ||
'<div class=" | '<div class="%s">[[%s|%s]]</div>', | ||
prevPage, prevLabel | prevClass, prevPage, prevLabel | ||
)) | )) | ||
else | else | ||
| Line 221: | Line 265: | ||
-- Use "class=" parameter in MediaWiki link syntax for proper styling | -- Use "class=" parameter in MediaWiki link syntax for proper styling | ||
table.insert(output, string.format( | table.insert(output, string.format( | ||
'<div class=" | '<div class="%s">[[%s|%s]]</div>', | ||
nextPage, nextLabel | nextClass, nextPage, nextLabel | ||
)) | )) | ||
else | else | ||
Revision as of 02:23, 10 May 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
]]
-- External dependencies
local ErrorHandling = require('Module:ErrorHandling')
local Blueprint = require('Module:LuaTemplateBlueprint')
-- MediaWiki global for linting
local mw = mw or {} -- This allows linting to work while still using the global mw in MediaWiki
local p = {}
p.elementName = "navigation"
-- Helper function to add this element to a template with custom configuration
-- @param template table The Blueprint template object
-- @param errorContext table The error context for safe require
-- @param options table Optional configuration overrides
function p.addToTemplate(template, errorContext, options)
options = options or {}
-- Only proceed if we're properly loaded
local self = errorContext and ErrorHandling.safeRequire(errorContext, 'Module:ElementNavigation', false) or p
if self and self.elementName then
-- Register this element with Blueprint
Blueprint.registerElement(self.elementName, self)
-- Add the element to the template
Blueprint.addElementToTemplate(template, 'navigation')
-- Set up configuration with defaults that can be overridden
template.config.navigation = {
autoDetect = options.autoDetect ~= nil and options.autoDetect or true,
patterns = options.patterns or self.defaultConfig.patterns,
prevField = options.prevField or self.defaultConfig.prevField,
nextField = options.nextField or self.defaultConfig.nextField,
prevLabel = options.prevLabel or "← %s",
nextLabel = options.nextLabel or "%s →",
prevClass = options.prevClass or self.defaultConfig.prevClass,
nextClass = options.nextClass or self.defaultConfig.nextClass,
rowHeight = options.rowHeight or self.defaultConfig.rowHeight
}
return true
end
return false
end
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 →",
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()
-- Get configuration from template
local config = template.config.navigation or {}
-- 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 or "element-navigation-prev"
local nextClass = config.nextClass or "element-navigation-next"
local rawPrevLabel = config.prevLabel or "← Previous"
local rawNextLabel = config.nextLabel or "Next →"
local rowHeight = config.rowHeight or "40"
-- Compute dynamic labels based on page names
local prevLabel = ""
if prevPage then
if rawPrevLabel:find("%%s") then
prevLabel = string.format(rawPrevLabel, prevPage)
else
prevLabel = rawPrevLabel
end
end
local nextLabel = ""
if nextPage then
if rawNextLabel:find("%%s") then
nextLabel = string.format(rawNextLabel, nextPage)
else
nextLabel = rawNextLabel
end
end
-- Create navigation row
local output = {
'|-',
'| colspan="2" height="' .. rowHeight .. '" valign="middle" |'
}
table.insert(output, '<div class="element-navigation-container">')
-- Add previous link
if prevPage then
-- Use "class=" parameter in MediaWiki link syntax for proper styling
table.insert(output, string.format(
'<div class="%s">[[%s|%s]]</div>',
prevClass, prevPage, prevLabel
))
else
table.insert(output, " ")
end
-- Add next link cell
-- Removed separate next cell; using single colspan cell
if nextPage then
-- Use "class=" parameter in MediaWiki link syntax for proper styling
table.insert(output, string.format(
'<div class="%s">[[%s|%s]]</div>',
nextClass, nextPage, nextLabel
))
else
table.insert(output, " ")
end
table.insert(output, '</div>')
return table.concat(output, "\n")
end)
end
end
return p