Module:InfoboxLite

From Dune: Awakening Community Wiki
Jump to navigation Jump to search

Module:InfoboxLite

A lightweight variant of Module:Infobox for infoboxes that don't need the full feature set. Identical interface but with the following removed:

  • TemplateStyles loading
  • child / subbox support
  • autoheaders
  • Navbar
  • Italic title
  • Nested wikitable support

These were removed because there are currently no use cases for them on the wiki, and calling them 150~ times on content-heavy pages (such as Research) was causing Lua CPU timeouts.

Row limit

Numbered parameters (data, header, label, image, subheader) are capped, defined by local MAX_ROWS = X below, where X is the defined limit.

If a parameter beyond this limit is detected, up to the original Module:Infobox limit of 50 (e.g. |header11=), the infobox will not render, and an error message will be shown in its place.

Use Module:Infobox directly if you genuinely need more than 10 rows.

Usage

Use Template:InfoboxLite in place of Template:Infobox. All parameters are identical.


local p = {}

local MAX_ROWS = 10
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^%]]*%]%]'

local ustring = mw.ustring
local html = mw.html
local text = mw.text
local tinsert = table.insert

-- ---------- Helpers ----------

-- Strip categories since category links count as non-whitespace, which can cause empty infobox fields to show up
local function hasVisibleText(s)
    if not s then return false end

    if s:find('%[%[') then s = ustring.gsub(s, category_in_empty_row_pattern, '') end

    return s:find('%S') ~= nil
end

-- Ensure no input fields are above the specified cap
local function checkOverflows(args)
    for i = MAX_ROWS + 1, 50 do
        if args['data' .. i] then
            return '|data' .. i .. '= exceeds the maximum of ' .. MAX_ROWS .. ' rows.'

        elseif args['header' .. i] then
            return '|header' .. i .. '= exceeds the maximum of ' .. MAX_ROWS .. ' rows.'

        elseif args['subheader' .. i] then
            return '|subheader' .. i .. '= exceeds the maximum of ' .. MAX_ROWS .. ' subheaders.'

        elseif args['image' .. i] then
            return '|image' .. i .. '= exceeds the maximum of ' .. MAX_ROWS .. ' images.'

        end
    end

    return nil
end

-- Process bullet lists to ensure they render correctly
local function fixChildBoxes(s, tt)
    s = ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
    s = ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
    s = ustring.gsub(s, '^([%*#;:])', '\n%1')
    return s
end

-- ---------- Rendering ----------

local function renderAboveRow(root, args)
    local above = args.above
    if not above then return end

    root
        :tag('tr')
        :tag('th')
            :attr('colspan', '2')
            :addClass('infobox-above')
            :addClass(args.aboveclass)
            :cssText(args.abovestyle)
            :wikitext(fixChildBoxes(above, 'th'))
end

local function renderSubheaders(root, args)
    if args.subheader and not args.subheader1 then args.subheader1 = args.subheader end

    local subheaderclass = args.subheaderclass
    local subheaderstyle = args.subheaderstyle

    for i = 1, MAX_ROWS do
        local val = args['subheader' .. i]

        if hasVisibleText(val) then
            root
                :tag('tr')
                :tag('td')
                    :attr('colspan', '2')
                    :addClass('infobox-subheader')
                    :addClass(subheaderclass)
                    :cssText(subheaderstyle)
                    :cssText(args['subheaderstyle' .. i])
                    :wikitext(fixChildBoxes(val, 'td'))
        end
    end
end

local function renderImages(root, args)
    if args.image and not args.image1 then args.image1 = args.image end
    if args.caption and not args.caption1 then args.caption1 = args.caption end

    local captionstyle = args.captionstyle
    local imageclass = args.imageclass
    local imagestyle = args.imagestyle

    for i = 1, MAX_ROWS do
        local img = args['image' .. i]

        if hasVisibleText(img) then
            local caption = args['caption' .. i]
            local content

            if caption then
                local data = html.create()
                data:wikitext(img)
                data:tag('div')
                    :addClass('infobox-caption')
                    :cssText(captionstyle)
                    :wikitext(caption)
                content = tostring(data)
            end

            root
                :tag('tr')
                    :addClass(args['imagerowclass' .. i])
                :tag('td')
                    :attr('colspan', '2')
                    :addClass('infobox-image')
                    :addClass(imageclass)
                    :cssText(imagestyle)
                    :wikitext(fixChildBoxes(content or img, 'td'))
        end
    end
end

local function renderRows(root, args, empty_row_categories)
    local headerclass = args.headerclass
    local headerstyle = args.headerstyle
    local labelstyle  = args.labelstyle
    local datastyle   = args.datastyle

    for i = 1, MAX_ROWS do
        local header = args['header' .. i]
        local data = args['data' .. i]
        local label = args['label' .. i]

        local rowclass = args['rowclass' .. i]
        local rowstyle = args['rowstyle' .. i]
        local rowcellstyle = args['rowcellstyle' .. i]

        if header and header ~= '_BLANK_' then
            root
                :tag('tr')
                    :addClass(rowclass)
                    :cssText(rowstyle)
                :tag('th')
                    :attr('colspan', '2')
                    :addClass('infobox-header')
                    :addClass(headerclass)
                    :cssText(headerstyle)
                    :cssText(rowcellstyle)
                    :wikitext(fixChildBoxes(header, 'th'))

        elseif hasVisibleText(data) then
            local row = root:tag('tr')

            row:addClass(rowclass)
            row:cssText(rowstyle)

            if label then
                row
                    :tag('th')
                        :attr('scope', 'row')
                        :addClass('infobox-label')
                        :cssText(labelstyle)
                        :cssText(rowcellstyle)
                        :wikitext(label)
                        :done()
            end

            local dataCell = row:tag('td')

            dataCell
                :attr('colspan', not label and '2' or nil)
                :addClass(not label and 'infobox-full-data' or 'infobox-data')
                :addClass(args['class' .. i])
                :cssText(datastyle)
                :cssText(rowcellstyle)
                :wikitext(fixChildBoxes(data, 'td'))

        else tinsert(empty_row_categories, data or '') end
    end
end

local function renderBelowRow(root, args)
    local below = args.below
    if not below then return end

    root
        :tag('tr')
        :tag('td')
            :attr('colspan', '2')
            :addClass('infobox-below')
            :addClass(args.belowclass)
            :cssText(args.belowstyle)
            :wikitext(fixChildBoxes(below, 'td'))
end

local function renderEmptyRowCategories(root, empty_row_categories)
    for _, s in ipairs(empty_row_categories) do root:wikitext(s) end
end

-- ---------- Main ----------

local function _infobox(origArgs)
    local args = {}

    for k, v in pairs(origArgs) do
        v = text.trim(v)
        if v ~= '' then args[k] = v end
    end

    local overflow = checkOverflows(args)
    if overflow then return '<strong class="error">⚠ InfoboxLite error: ' .. overflow .. '</strong>' end

    local empty_row_categories = {}

    local root = html.create('table')

    root
        :addClass('infobox')
        :addClass(args.bodyclass)
        :cssText(args.bodystyle)

    if args.title then
        root
            :tag('caption')
                :addClass('infobox-title')
                :addClass(args.titleclass)
                :cssText(args.titlestyle)
                :wikitext(args.title)
    end

    renderAboveRow(root, args)
    renderSubheaders(root, args)
    renderImages(root, args)
    renderRows(root, args, empty_row_categories)
    renderBelowRow(root, args)
    renderEmptyRowCategories(root, empty_row_categories)

    return tostring(root)
end

function p.infobox(frame)
    local origArgs

    if frame == mw.getCurrentFrame() then origArgs = frame:getParent().args
    else origArgs = frame end

    return _infobox(origArgs)
end

function p.infoboxTemplate(frame)
    return _infobox(frame.args)
end

return p