PC Gaming Shelter
An archive dedicated to preserving PC Gaming history and more

Module:Utils: Difference between revisions

From PC Gaming Shelter
No edit summary
No edit summary
Line 180: Line 180:
         local secName = propToSection[key]
         local secName = propToSection[key]
         if secName and not emittedSections[secName] then
         if secName and not emittedSections[secName] then
             infoboxData:tag('div'):addClass('infobox-section')
             infoboxData:tag('div'):addClass('infobox-section-header p-2')
                 :attr{style = 'order:' .. tostring(orderNumber)}
                 :attr{style = 'order:' .. tostring(orderNumber)}
                 :wikitext(secName)
                 :wikitext(secName)

Revision as of 23:35, 22 March 2026

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

local p = {}

local function splitBy(str, sep)
	local t = {}
	for part in str:gmatch("[^" .. sep .. "]+") do
		table.insert(t, mw.text.trim(part))
	end
	return t
end

function p.setCategory(categoryString)
	if not categoryString then return end
	local category = string.format('[[Category:%s]]', categoryString)
	return category
end

function p.setProperties(propMap, args)
	local props = {}
	for argName, def in pairs(propMap) do
    	local val = args[argName]
		if val ~= nil then
			local propKey = def[1]
			local sep     = def[2] or nil
			if type(val) == "string" and #def > 1 and sep == "file" then
				if val:find("^%w+:%s*") then
					val = val:gsub("^%w+:%s*", "")  -- strip namespace, we add it later
            	end
				val = string.format("File:%s", val)
			elseif type(val) == "string" and #def > 1 and sep then
				val = splitBy(val, sep)
			end
			props[propKey] = val
		end
	end
	return props
end

function p.infoboxSectionClasses()
	return {
		['onecolumn'] = 'infobox-section d-flex flex-column py-1 border-0 rounded-0 p-1',
		['twocolumns'] = 'infobox-section d-flex flex-column flex-sm-row border-0 rounded-0 p-1'
	}
end

local function formatDate(input)
    local year, month, day = input:match("(%d+)%-(%d+)%-(%d+)")
    local time = os.time({
        year = tonumber(year),
        month = tonumber(month),
        day = tonumber(day)
    })
    return os.date("%B %d, %Y", time):gsub("^%a+ (%d+)", function(d)
        return os.date("%B", time) .. " " .. tonumber(d)
    end)
end


function p.renderInfoboxValue(frame)
	local args = frame:getParent().args or {}
	local val = args[1] or nil
	local sep = args[2] or nil
	local fmt = args[3] or nil
	
	if not val then return end
	if not sep and not fmt then return val end
  	
  	local html = mw.html.create()
  	local ul = html:tag('ul')
  	
	local list = splitBy(val, sep) or {}
	for _, subStr in ipairs(list) do
		local printout = ''
		if fmt == "wikilink" then
			printout = string.format('[[%s]]', subStr)
		elseif fmt == "external" then
			printout = string.format('[%s %s]', subStr, linkText)
		elseif fmt == "date" then
			printout = formatDate(subStr)
		else
			printout = subStr
		end
		ul:tag('li'):wikitext(printout)
	end	

    return html
end

-- ------------------------------------------------------------------
-- |  p.createInfobox()
-- ------------------------------------------------------------------
-- Build an infobox from the template arguments that called this module.
--
-- Parameters
-- ----------
-- * frame      – The current Frame object.
-- * cat        – Main category string to add before the page content.
-- * properties – A mapping of user‑argument names → SMW property keys
--                (optionally followed by a separator).  These are used
--                to set SMW properties via `mw.smw.set()`.
-- * order      – An ordered list that defines the order in which
--                sections appear inside the infobox.  Each entry must
--                match a key in `properties` or in the `infodata`
--                table below.
-- * sections   - A plan of sections and properties.
-- * subobjects - The table with names of templates responsible for subobjects creation
--                The module expects the the parent template parameter name **is equal** 
--                to the name of the template:
--                `...`
--                `|TemplateName={{TemplateName|...}}{{TemplateName|...}}`
-- Returns
-- -------
-- An `mw.html` object that contains:
--
--   * The main category (via `p.setCategory(cat)`),
--   * A fully populated infobox `<div>` with sections in the order
--     specified by **order** and styled according to **infodata**,
--
-- The returned HTML is rendered directly into the page where the module
-- was invoked.
--------------------------------------------------------------------

function p.createInfobox(frame, category, properties, order, sections, data, subobjects)
	local currentTitle = mw.title.getCurrentTitle().fullText
	local args = frame:getParent().args

	-- SMW properties
	local propMap = properties
	local props = p.setProperties(propMap, args) or {}
	mw.smw.set(props)

	-- Main category
	local category = p.setCategory(cat)
	
	-- Assemble
	local html = mw.html.create()
	-- Infobox
	local infoboxData = html:tag('div'):addClass('infobox d-flex flex-col card shadow-none rounded-0 bg-transparent mb-4 float-sm-right')
	-- Header @WikiVisor: Probably unnecessary
	-- infoboxData:tag('div')
	--	:addClass('infobox-header card-header rounded-0 text-center')
	--	:tag('div'):wikitext(currentTitle)
	-- Cover image
	local coverImage = args['Image'] or nil
	if coverImage then
		infoboxData:tag('div')
			:addClass('infobox-img card-img border-0')
			:wikitext(string.format('[[File:%s|560px|class=pageimage]]', coverImage)):done()
	end
	-- Other props
	local propOrder = order
	-- Infobox sections
	local sectionOrder = sections
	local sectionFormat = data
	-- Helper
	local propToSection = {}
    for secName, props in pairs(sections) do
        for _, p in ipairs(props) do
            propToSection[p] = secName
        end
    end
	-- Helper: Render the value
	local function renderValue(frame, key, rawValue, fmt)
		if fmt.template == 'render' then
			return frame:expandTemplate{
				title = 'render',
				args = {
					string.format(fmt.value, rawValue),
					fmt.separator,
					fmt.valuefmt
				}
			}
		else
			return string.format(fmt.value, rawValue)
		end
	end
	-- Render infobox sections
	local orderNumber = 1
	local emittedSections = {}
    for _, key in ipairs(propOrder) do
        local secName = propToSection[key]
        if secName and not emittedSections[secName] then
            infoboxData:tag('div'):addClass('infobox-section-header p-2')
                :attr{style = 'order:' .. tostring(orderNumber)}
                :wikitext(secName)
            orderNumber = orderNumber + 1
            emittedSections[secName] = true
        end
        local rawValue = args[key]
        if rawValue then
            local fmt = sectionFormat[key] or { value = '%s' }
            local renderedLabel   = string.format(fmt.label or '%s', key)
            local renderedValue   = renderValue(frame, key, rawValue, fmt)
            infoboxData:tag('div'):css({order = orderNumber}):addClass(fmt.class)
                :tag('div'):addClass('infobox-label px-2'):wikitext(renderedLabel):done()
                :tag('div'):addClass('infobox-value px-2'):wikitext(renderedValue):done()
            orderNumber = orderNumber + 1
        end
    end
	-- SMW subobjects
	if subobjects and #subobjects > 0 then
        for _, obj in ipairs(subobjects) do
			local key      = obj.subobject
			local label    = obj.label or key
			local order    = obj.order or '500'
			local subobjectData = args[key]
			if key and subobjectData then
				infoboxData:tag('div'):css({ ['order'] = order})
					:addClass(p.infoboxSectionClasses()['twocolumns'])
					:tag('div'):addClass('infobox-label px-2')
						:wikitext(label)
					:done()
					:tag('div'):addClass('infobox-value px-2')
						:tag('ul'):wikitext(subobjectData):done()
					:done()
			end
		end
	end
	-- Printout
	html
		:wikitext(category)

	return html
end

return p