Documentation for this module may be created at Module:Multi-section template/doc

-- This module takes a list of parameters (of two types) and a template name
-- It can then create multiple layers of that same template under section headers
-- When a page wants to use the multi-layered template, it can define section names
--- with "sec 1", "sec 2" etc. It can give a title to the tabber with "tabber"
-- To assign values for each section, they can be prefixed by their tab number,
--- e.g. "1 name"
-- Parameters can be "override" or "additive".
-- "override" means "1 name" will be used instead of "name".
-- "additive" means "1 name" will appear a line after "name".
-- Section names can be formatted from given parameters (as long as they are
--- unique to the tab)

p = {}

local getArgs = require('Dev:Arguments').getArgs

local params = {
    override = {},
    additive = {}
}

local templatename
local sectionheaderformat = {}
-- format has parameters put in [], e.g. "#[bestiary]"
-- first tab's would be "#{{{1 bestiary}}}", second "#{{{2 bestiary}}}" etc.
-- multiple formats can be used, semi-colon separated, in order of priority
-- a format will only be used if all the contained parameters are used
-- "tab 1" etc. has highest priority and doesn't need to be stated

function p.main(frame)
    -- PARAMETERS FOR THE MODULE
    --- "override parameters", a ";"-separated list of override parameters
    ---- or a {} if used in Lua
    --- "additive parameters", a ";"-separated list of additive parameters
    ---- or a {} if used in Lua
    --- "default tabber title", the default tabber title
    --- "template pagename", the page name of the template
    --- "template type", type of template. "infobox"
    --- "tab name format", a ";"-separated list of formats in priority order
    local args = getArgs(frame)

    -- split a string by semicolons, filter out whitespace-only parts, and trim
    -- whitespace
    local function splitter(str)
        if type(str) ~= "string" then
            return str
        end

        local parts = {}

        for part in mw.text.gsplit(str, ";") do
            if not part:find("^%s+$") then
                table.insert(parts, mw.text.trim(part))
            end
        end

        return parts
    end

    for _, v in ipairs{"override", "additive"} do
        local tmp = args[v .. " parameters"]

        if tmp then
            params[v] = splitter(tmp)
        end
    end

    if args["section header format"] then
        local formats = splitter(args["section header format"])

        for _, f in ipairs(formats) do
            local parameters = {}
            local format, count = f:gsub("%[([^%]]*)%]", function (capture)
                -- this overwrites `parameters` on each iteration, effectively
                -- storing the last capture in a table. might be a bug? idk
                parameters = {capture}

                return "%s"
            end)

            if count == 0 then
                return require "Module:HF".err("Section format '" .. format .. "' contains no parameters. Parameters are enclosed in square brackets.")
            end

            table.insert(sectionheaderformat, {format = format, parameters = parameters})
        end
    end

    templatename = args["template pagename"]

    local struct = p.structure(args)

    return p.create(struct)
end

-- The creation of the table structure
function p.structure(args, prefix)
    if not prefix then
        prefix = {}
    end

    local o = {}
    local thisprefix = prefix[#prefix] and (prefix[#prefix] .. " ") or ""

    if p.secname(args, (thisprefix .. 1)) then
        o.section = args -- likely is causing an error
        o.sect = p.secname(args, mw.text.trim(thisprefix))
        o._depth = #prefix
    end

    for i = 1, 255 do
        table.insert(prefix, thisprefix .. i)

        if p.secname(args, (thisprefix .. i .. " " .. 1)) then
            table.insert(o, p.structure(args, prefix))
        else
            table.insert(o, p.consolidateArgs(args, prefix))
        end

        table.remove(prefix)

        if not p.secname(args, (thisprefix .. i + 1)) then
            break
        end
    end

    return o
end

-- The consolidation of template parameters into individual tables
function p.consolidateArgs(a, prefix)
    -- prefix is an array of prefixed from least to most important
    local o = {}
    o._depth = #prefix

    -- returned is a table of final args
    for _, parameter in ipairs(params.override) do
        local value

        for j = #prefix, 1, -1 do
            value = a[prefix[j] .. " " .. parameter]

            if value then
                break
            end
        end

        o[parameter] = value or a[parameter]
    end

    for _, parameter in ipairs(params.additive) do
        local list = {}
        local value

        if a[parameter] then
            table.insert(list, a[parameter])
        end

        for _, val in ipairs(prefix) do
            local k = val .. " " .. parameter

            if a[k] then
                table.insert(list, a[k])
            end
        end

        if #list > 0 then
            value = list[1]

            for j = 2, #list do
                value = value .. "<br/>" .. list[j]
            end
        end

        o[parameter] = value
    end

    o.sect = p.secname(a, prefix[#prefix])

    return o
end

-- The determining of section names
function p.secname(a, prefix)
    -- a is the args table
    -- prefix is the prefix /STRING/ -- not the table
    if prefix == "" then
        return "You should never see this"
    end

    local thissecname = a[mw.text.trim("sec " .. prefix)]

    if not thissecname then
        for i, thisformat in ipairs(sectionheaderformat) do
            local allparams = true

            for _, p in ipairs(thisformat.parameters) do
                if not a[mw.text.trim(prefix .. " " .. p)] then
                    allparams = false
                end
            end

            if allparams then
                local secparams = {}

                for _, p in ipairs(thisformat.parameters) do
                    secparams[i] = a[mw.text.trim(prefix .. " " .. p)]
                end

                thissecname = thisformat.format:format(unpack(secparams))

                break
            end
        end
    end

    return thissecname
end

-- The output
function p.create(data)
    local container = mw.html.create("div") -- things are just easier with a container

    for _, val in ipairs(data) do
        if #data ~= 1 then
            container:tag("h" .. (val._depth + 2)):wikitext(val.sect)
        end

        if val.section then
            container:node(p.create(val))
        else
            container:wikitext(p.sect(val))
        end
    end

    return container
end

-- The template expanding
function p.sect(data)
    if templatename:sub(1, 7) == "Module:" then
        return require(templatename).init(data)
    end

    return mw.getCurrentFrame():expandTemplate({
        title = templatename,
        args = data
    })
end

return p
Community content is available under CC-BY-SA unless otherwise noted.