Jump to content

Module:ElementNavigation: Difference between revisions

// 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       = "has_previous",
     prevField = "has_previous",
     nextField       = "has_next",
     nextField = "has_next",
     prevClass       = "element-navigation-prev",
     prevClass = "element-navigation-prev",
     nextClass       = "element-navigation-next",
     nextClass = "element-navigation-next",
    linkPrevClass  = "element-nav-prev",
     prevLabel = "← Previous",
    linkNextClass  = "element-nav-next",
     nextLabel = "Next →",
     prevLabel       = "← Previous",
     rowHeight = "40"
     nextLabel       = "Next →",
     rowHeight       = "40"
}
}


Line 119: Line 117:
          
          
         return execute(function()
         return execute(function()
             -- Merge default config with template overrides
             -- Get configuration from template
             local rawCfg = template.config.navigation or {}
             local config = 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 183: Line 174:
              
              
             -- Get styling options
             -- Get styling options
            local prevClass     = config.prevClass
local prevClass = config.prevClass or "element-navigation-prev"
            local nextClass     = config.nextClass
local nextClass = config.nextClass or "element-navigation-next"
            local linkPrevClass = config.linkPrevClass
             local prevLabel = config.prevLabel or "← Previous"
            local linkNextClass = config.linkNextClass
             local nextLabel = config.nextLabel or "Next →"
             local prevLabel     = config.prevLabel
             local rowHeight = config.rowHeight or "40"
             local nextLabel     = config.nextLabel
             local rowHeight     = config.rowHeight
              
              
             -- 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" |'
             }
             }
Line 201: Line 189:
             if prevPage then
             if prevPage then
                 table.insert(output, string.format(
                 table.insert(output, string.format(
                     '<div class="%s">[[%s|%s]]</div>',
                     '<div class="element-navigation-prev">[[%s|%s]]</div>',
                     linkPrevClass, prevPage, prevLabel
                     prevPage, prevLabel
                 ))
                 ))
             else
             else
Line 213: Line 201:
             if nextPage then
             if nextPage then
                 table.insert(output, string.format(
                 table.insert(output, string.format(
                     '<div class="%s">[[%s|%s]]</div>',
                     '<div class="element-navigation-next">[[%s|%s]]</div>',
                     linkNextClass, nextPage, nextLabel
                     nextPage, nextLabel
                 ))
                 ))
             else
             else

Revision as of 02:59, 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",
    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 prevLabel = config.prevLabel or "← Previous"
            local nextLabel = config.nextLabel or "Next →"
            local rowHeight = config.rowHeight or "40"
            
            -- Create navigation row
            local output = {
                '|-',
                '| class="' .. prevClass .. '" height="' .. rowHeight .. '" valign="middle" |'
            }
            
            -- Add previous link
            if prevPage then
                table.insert(output, string.format(
                    '<div class="element-navigation-prev">[[%s|%s]]</div>',
                    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="element-navigation-next">[[%s|%s]]</div>',
                    nextPage, nextLabel
                ))
            else
                table.insert(output, "&nbsp;")
            end
            
            return table.concat(output, "\n")
        end)
    end
end

return p