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 common = require("Module:Common")

local ustring = mw.ustring
local html = mw.html

-- ---------- Data ----------

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

-- ---------- 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_EMPTY_ROW_REGEX, "") 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, "rows"
        elseif args["header" .. i] then return "header", i, "rows"
        elseif args["subheader" .. i] then return "subheader", i, "subheaders"
        elseif args["image" .. i] then return "image", i, "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 empty_row_categories[#empty_row_categories + 1] = 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 = common.trim(v)
        if v ~= "" then args[k] = v end
    end

    local field, idx, noun = checkOverflows(args)
    if field then return common.error("%s%s exceeds the maximum of %s %s.", field, idx, MAX_ROWS, noun) 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

-- ---------- Interface ----------

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