Jump to content

Module:InfoboxBasic: Difference between revisions

From Yusupov's House
No edit summary
No edit summary
 
(One intermediate revision by the same user not shown)
Line 4: Line 4:


local function isBlank(v) return v == nil or trim(tostring(v)) == '' end
local function isBlank(v) return v == nil or trim(tostring(v)) == '' end
local function labelFromKey(k) k = k:gsub('_',' '); return trim(k) end
local function labelFromKey(k) return trim((k:gsub('_',' '))) end
 
-- Extract optional numeric prefix to force ordering, e.g. "01 Born", "002_Born"
local function splitOrderPrefix(key)
    local n, rest = tostring(key):match('^%s*(%d+)[%s_%-%.]*(.+)$')
    if n then
        return tonumber(n), trim(rest)
    end
    return nil, key
end


function p.render(frame)
function p.render(frame)
Line 10: Line 19:
     local args  = parent.args or {}  -- never nil
     local args  = parent.args or {}  -- never nil


     local title = args.name or args.Name or args.title or args.Title or 'Infobox'
     local title   = args.name or args.Name or args.title or args.Title or 'Infobox'
     local image = args.image or args.Image
     local image   = args.image or args.Image
     local color = args.color or args.Color or args.colour or args.Colour
    local caption = args.caption or args.Caption
     local color   = args.color or args.Color or args.colour or args.Colour


     -- Table
     -- Table
Line 21: Line 31:
         :css('margin','0 0 1em 1em')
         :css('margin','0 0 1em 1em')


     -- Title (centered, 125%). Background only if |color= was set (CSS reads --ib-title-bg)
     -- Title
     local th = tbl:tag('tr'):tag('th')
     local th = tbl:tag('tr'):tag('th')
         :attr('colspan','2')
         :attr('colspan','2')
Line 31: Line 41:
     if not isBlank(color) then
     if not isBlank(color) then
         th:addClass('has-title-color')
         th:addClass('has-title-color')
         th:css('--ib-title-bg', color)
         th:css('--ib-title-bg', color) -- only used by light-mode CSS
     end
     end


     -- Optional image row (full width via CSS)
     -- Optional image
     if not isBlank(image) then
     if not isBlank(image) then
         tbl:tag('tr')
         local cell = tbl:tag('tr'):tag('td')
            :tag('td')
             :attr('colspan','2')
             :attr('colspan','2')
             :addClass('infobox-image-cell')
             :addClass('infobox-image-cell')
            :wikitext('[[File:' .. image .. '|frameless]]')
        cell:wikitext('[[File:' .. image .. '|frameless]]')
 
        if not isBlank(caption) then
            cell:tag('div')
                :css('text-align','center')
                :css('font-size','90%')
                :css('font-style','italic')
                :css('margin-top','0.2em')
                :wikitext(caption)
        end
     end
     end


     -- Keys to skip (already handled above)
     -- Keys to skip (already handled)
     local skip = {
     local skip = {
         name=true, Name=true, title=true, Title=true,
         name=true, Name=true, title=true, Title=true,
         image=true, Image=true,
         image=true, Image=true,
        caption=true, Caption=true,
         color=true, Color=true, colour=true, Colour=true,
         color=true, Color=true, colour=true, Colour=true,
         class=true, style=true
         class=true, style=true
Line 57: Line 76:
     end
     end


     -- Preserve editor's order: prefer getArgumentNames() (source order on modern MW)
     -- Build a stable order map from the editor's source order if possible
     if parent.getArgumentNames then
    local sourceOrder = {}
         for _, key in ipairs(parent:getArgumentNames()) do
     if parent.argumentPairs then
             if not skip[key] and not tonumber(key) then
        local temp = {}
                local v = args[key]
         for key, _ in parent:argumentPairs() do
                if not isBlank(v) then addRow(key, v) end
             temp[#temp+1] = key
             end
        end
        for i = #temp, 1, -1 do
             sourceOrder[temp[i]] = i
         end
         end
     elseif parent.argumentPairs then
     elseif parent.getArgumentNames then
         -- Fallback: collect then reverse (argumentPairs often yields last→first)
         local names = parent:getArgumentNames() or {}
        local seq = {}
         for i, key in ipairs(names) do
         for key, value in parent:argumentPairs() do
             sourceOrder[key] = i
             if not skip[key] and not tonumber(key) and not isBlank(value) then
                seq[#seq+1] = { key = key, value = value }
            end
         end
         end
        for i = #seq, 1, -1 do
    end
             addRow(seq[i].key, seq[i].value)
 
    -- Collect rows with computed sort keys
    local rows = {}
    for key, value in pairs(args) do
        if not skip[key] and not tonumber(key) and not isBlank(value) then
             local forcedN, displayKey = splitOrderPrefix(key)
            local srcIndex = sourceOrder[key] or 10^9
            rows[#rows+1] = {
                forced = forcedN or 10^8,
                src    = srcIndex,
                key    = displayKey,
                rawkey = key,
                value  = value
            }
         end
         end
     else
     end
        -- Very old fallback: best effort (order not guaranteed)
 
        for k, v in pairs(args) do
    -- Sort: first by explicit numeric prefix (if any), then by source order
            if not skip[k] and not tonumber(k) and not isBlank(v) then
    table.sort(rows, function(a,b)
                addRow(k, v)
        if a.forced ~= b.forced then
             end
             return a.forced < b.forced
         end
         end
        return a.src < b.src
    end)
    -- Render rows
    for _, r in ipairs(rows) do
        addRow(r.key, r.value)
     end
     end



Latest revision as of 00:30, 2 October 2025

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

-- Module:InfoboxBasic
local p = {}
local trim = mw.text.trim

local function isBlank(v) return v == nil or trim(tostring(v)) == '' end
local function labelFromKey(k) return trim((k:gsub('_',' '))) end

-- Extract optional numeric prefix to force ordering, e.g. "01 Born", "002_Born"
local function splitOrderPrefix(key)
    local n, rest = tostring(key):match('^%s*(%d+)[%s_%-%.]*(.+)$')
    if n then
        return tonumber(n), trim(rest)
    end
    return nil, key
end

function p.render(frame)
    local parent = frame:getParent() or frame
    local args   = parent.args or {}   -- never nil

    local title   = args.name or args.Name or args.title or args.Title or 'Infobox'
    local image   = args.image or args.Image
    local caption = args.caption or args.Caption
    local color   = args.color or args.Color or args.colour or args.Colour

    -- Table
    local tbl = mw.html.create('table')
        :addClass('infobox')
        :css('float','right')
        :css('clear','right')
        :css('margin','0 0 1em 1em')

    -- Title
    local th = tbl:tag('tr'):tag('th')
        :attr('colspan','2')
        :addClass('infobox-title')
        :css('text-align','center')
        :css('font-size','125%')
        :wikitext(title)

    if not isBlank(color) then
        th:addClass('has-title-color')
        th:css('--ib-title-bg', color) -- only used by light-mode CSS
    end

    -- Optional image
    if not isBlank(image) then
        local cell = tbl:tag('tr'):tag('td')
            :attr('colspan','2')
            :addClass('infobox-image-cell')
        cell:wikitext('[[File:' .. image .. '|frameless]]')

        if not isBlank(caption) then
            cell:tag('div')
                :css('text-align','center')
                :css('font-size','90%')
                :css('font-style','italic')
                :css('margin-top','0.2em')
                :wikitext(caption)
        end
    end

    -- Keys to skip (already handled)
    local skip = {
        name=true, Name=true, title=true, Title=true,
        image=true, Image=true,
        caption=true, Caption=true,
        color=true, Color=true, colour=true, Colour=true,
        class=true, style=true
    }

    local function addRow(label, value)
        local row = tbl:tag('tr')
        row:tag('th'):attr('scope','row'):wikitext(labelFromKey(label))
        row:tag('td'):wikitext(value)
    end

    -- Build a stable order map from the editor's source order if possible
    local sourceOrder = {}
    if parent.argumentPairs then
        local temp = {}
        for key, _ in parent:argumentPairs() do
            temp[#temp+1] = key
        end
        for i = #temp, 1, -1 do
            sourceOrder[temp[i]] = i
        end
    elseif parent.getArgumentNames then
        local names = parent:getArgumentNames() or {}
        for i, key in ipairs(names) do
            sourceOrder[key] = i
        end
    end

    -- Collect rows with computed sort keys
    local rows = {}
    for key, value in pairs(args) do
        if not skip[key] and not tonumber(key) and not isBlank(value) then
            local forcedN, displayKey = splitOrderPrefix(key)
            local srcIndex = sourceOrder[key] or 10^9
            rows[#rows+1] = {
                forced = forcedN or 10^8,
                src    = srcIndex,
                key    = displayKey,
                rawkey = key,
                value  = value
            }
        end
    end

    -- Sort: first by explicit numeric prefix (if any), then by source order
    table.sort(rows, function(a,b)
        if a.forced ~= b.forced then
            return a.forced < b.forced
        end
        return a.src < b.src
    end)

    -- Render rows
    for _, r in ipairs(rows) do
        addRow(r.key, r.value)
    end

    return tostring(tbl)
end

return p