Module:Navbox

From Remilia Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Navbox/doc

--------------------------------------------------------------------------------
-- Module:Navbox
-- Navigation box system for linking related articles.
--
-- INSTALLATION:
-- 1. Create page: Module:Navbox
-- 2. Paste this entire file
-- 3. Create TemplateStyles page: Module:Navbox/styles.css
--
-- USAGE:
-- {{#invoke:Navbox|navbox
--   |name = Navbox Name
--   |title = [[Topic]] Navigation
--   |group1 = Category | list1 = [[Link1]] • [[Link2]]
-- }}
--
-- @author Remilia Wiki
-- @license MIT
--------------------------------------------------------------------------------

local p = {}

--------------------------------------------------------------------------------
-- CONFIGURATION
--------------------------------------------------------------------------------

local MAX_GROUPS = 20  -- Maximum number of groups supported

--------------------------------------------------------------------------------
-- UTILITY FUNCTIONS
--------------------------------------------------------------------------------

local function trim(s)
    if not s then return nil end
    return tostring(s):match("^%s*(.-)%s*$")
end

local function isEmpty(s)
    if s == nil then return true end
    if type(s) == 'string' then
        return trim(s) == ''
    end
    return false
end

local function hasValue(s)
    return not isEmpty(s)
end

local function getArg(args, name, default)
    local val = args[name]
    if isEmpty(val) then return default end
    return trim(val)
end

local function getFrameArgs(frame)
    local args = {}
    local parentArgs = frame:getParent() and frame:getParent().args or {}
    local directArgs = frame.args or {}
    for k, v in pairs(directArgs) do args[k] = v end
    for k, v in pairs(parentArgs) do args[k] = v end
    return args
end

--------------------------------------------------------------------------------
-- NAVBOX
--------------------------------------------------------------------------------

function p.navbox(frame)
    local args = getFrameArgs(frame)

    local name = getArg(args, 'name')
    local title = getArg(args, 'title')
    local state = getArg(args, 'state', 'autocollapse')
    local above = getArg(args, 'above')
    local below = getArg(args, 'below')
    local bodyclass = getArg(args, 'bodyclass')
    local titleclass = getArg(args, 'titleclass')
    local listclass = getArg(args, 'listclass')

    if isEmpty(title) then
        return '<span class="error">Error: Navbox title required</span>'
    end

    -- Build the navbox table
    local tbl = mw.html.create('table')
        :addClass('navbox')

    if hasValue(bodyclass) then
        tbl:addClass(bodyclass)
    end

    -- Handle collapse state
    if state == 'collapsed' then
        tbl:addClass('mw-collapsed')
    elseif state == 'expanded' then
        tbl:addClass('navbox-expanded')
    else
        tbl:addClass('mw-collapsible')
    end

    tbl:attr('cellspacing', '0')

    -- Title row
    local titleRow = tbl:tag('tr')
    local titleCell = titleRow:tag('th')
        :addClass('navbox-title')
        :attr('colspan', '2')

    if hasValue(titleclass) then
        titleCell:addClass(titleclass)
    end

    -- V-T-E links
    if hasValue(name) then
        local vte = titleCell:tag('div')
            :addClass('navbox-vte')

        -- Build edit URL using mw.uri (not parser function)
        local editUrl = tostring(mw.uri.fullUrl('Template:' .. name, { action = 'edit' }))

        vte:wikitext('[')
        vte:tag('span')
            :addClass('noprint')
            :wikitext('[[Template:' .. name .. '|v]]')
        vte:wikitext(' · ')
        vte:tag('span')
            :addClass('noprint')
            :wikitext('[[Template talk:' .. name .. '|t]]')
        vte:wikitext(' · ')
        vte:tag('span')
            :addClass('noprint')
            :wikitext('[' .. editUrl .. ' e]')
        vte:wikitext(']')
    end

    -- Title text
    titleCell:tag('span')
        :addClass('navbox-title-text')
        :wikitext(title)

    -- Above row
    if hasValue(above) then
        local aboveRow = tbl:tag('tr')
        aboveRow:tag('td')
            :addClass('navbox-above')
            :attr('colspan', '2')
            :wikitext(above)
    end

    -- Group rows
    for i = 1, MAX_GROUPS do
        local group = getArg(args, 'group' .. i)
        local list = getArg(args, 'list' .. i)

        if hasValue(list) then
            local row = tbl:tag('tr')

            if hasValue(group) then
                row:tag('th')
                    :addClass('navbox-group')
                    :wikitext(group)

                local listCell = row:tag('td')
                    :addClass('navbox-list')
                if hasValue(listclass) then
                    listCell:addClass(listclass)
                end
                listCell:wikitext(list)
            else
                -- List without group header spans full width
                local listCell = row:tag('td')
                    :addClass('navbox-list')
                    :addClass('navbox-list-full')
                    :attr('colspan', '2')
                if hasValue(listclass) then
                    listCell:addClass(listclass)
                end
                listCell:wikitext(list)
            end
        elseif hasValue(group) then
            -- Group without list (header row)
            local row = tbl:tag('tr')
            row:tag('th')
                :addClass('navbox-subheader')
                :attr('colspan', '2')
                :wikitext(group)
        end
    end

    -- Below row
    if hasValue(below) then
        local belowRow = tbl:tag('tr')
        belowRow:tag('td')
            :addClass('navbox-below')
            :attr('colspan', '2')
            :wikitext(below)
    end

    return tostring(tbl)
end

--------------------------------------------------------------------------------
-- NAVBAR (V-T-E links only)
--------------------------------------------------------------------------------

function p.navbar(frame)
    local args = getFrameArgs(frame)

    local name = getArg(args, 1) or getArg(args, 'name')
    if isEmpty(name) then
        return '<span class="error">Error: Template name required</span>'
    end

    local mini = getArg(args, 'mini') == 'yes' or getArg(args, 'mini') == '1'

    local span = mw.html.create('span')
        :addClass('navbar')

    if mini then
        span:addClass('navbar-mini')
        span:wikitext('[[Template:' .. name .. '|v]]')
        span:wikitext(' · ')
        span:wikitext('[[Template talk:' .. name .. '|t]]')
        span:wikitext(' · ')
        span:wikitext('[{{fullurl:Template:' .. name .. '|action=edit}} e]')
    else
        span:wikitext('[[Template:' .. name .. '|view]] · ')
        span:wikitext('[[Template talk:' .. name .. '|talk]] · ')
        span:wikitext('[{{fullurl:Template:' .. name .. '|action=edit}} edit]')
    end

    return tostring(span)
end

--------------------------------------------------------------------------------
-- HLIST (Horizontal list)
--------------------------------------------------------------------------------

function p.hlist(frame)
    local args = getFrameArgs(frame)

    local separator = getArg(args, 'separator', ' • ')
    local items = {}

    for i = 1, 20 do
        local item = getArg(args, i)
        if hasValue(item) then
            table.insert(items, item)
        end
    end

    if #items == 0 then
        return ''
    end

    local span = mw.html.create('span')
        :addClass('hlist')
        :wikitext(table.concat(items, separator))

    return tostring(span)
end

--------------------------------------------------------------------------------
-- FLATLIST (Comma-separated list)
--------------------------------------------------------------------------------

function p.flatlist(frame)
    local args = getFrameArgs(frame)

    local separator = getArg(args, 'separator', ', ')
    local items = {}

    for i = 1, 20 do
        local item = getArg(args, i)
        if hasValue(item) then
            table.insert(items, item)
        end
    end

    if #items == 0 then
        return ''
    end

    local span = mw.html.create('span')
        :addClass('flatlist')
        :wikitext(table.concat(items, separator))

    return tostring(span)
end

return p