Jump to content

Module:ElementNavigation: Difference between revisions

// via Wikitext Extension for VSCode
// via Wikitext Extension for VSCode
Line 193: Line 193:
             -- Create navigation row
             -- Create navigation row
             local output = {
             local output = {
                 '|-',
                -- Add template-data-row class to <tr> for consistent row styling
                 '|- class="template-data-row"',
                 '| class="' .. prevClass .. '" height="' .. rowHeight .. '" valign="middle" |'
                 '| class="' .. prevClass .. '" height="' .. rowHeight .. '" valign="middle" |'
             }
             }

Revision as of 02:53, 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 = {
                -- Add template-data-row class to <tr> for consistent row styling
                '|- class="template-data-row"',
                '| 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, "&nbsp;")
            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, "&nbsp;")
            end
            
            return table.concat(output, "\n")
        end)
    end
end

return p