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

Module:ResourceList

From PC Gaming Shelter
Revision as of 12:05, 10 July 2026 by WikiVisor (talk | contribs)

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

local p = {}

local FIELDS = {
	{
		key = "resources",
		title = "Type",
		property = "Has resource type",
	},
	{
		key = "types",
		title = "Content",
		property = "Has content type",
	},
	{
		key = "langs",
		title = "Language",
		property = "Has language",
	},
	{
		key = "years",
		title = "Year",
		property = "Has date",
	},
}

local function trim(value)
	return mw.text.trim(tostring(value or ""))
end

local function valueText(value)
	if type(value) ~= "table" then
		return trim(value)
	end

	return trim(
		value.fulltext
			or value.fullText
			or value.label
			or value.text
			or value.displaytitle
			or value[1]
			or ""
	)
end

local function addValue(bucket, rawValue)
	local value = valueText(rawValue)

	if value == "" then
		return
	end

	local key = mw.ustring.lower(value)

	if bucket.counts[key] == nil then
		bucket.counts[key] = 0
		bucket.labels[key] = value
	end

	bucket.counts[key] = bucket.counts[key] + 1
end

local function collect(rows)
	local buckets = {}

	for _, field in ipairs(FIELDS) do
		buckets[field.key] = {
			counts = {},
			labels = {},
		}
	end

	for _, row in ipairs(rows) do
		for _, field in ipairs(FIELDS) do
			local values = row[field.key]
			local bucket = buckets[field.key]

			if type(values) == "table" then
				for _, value in ipairs(values) do
					addValue(bucket, value)
				end
			else
				addValue(bucket, values)
			end
		end
	end

	return buckets
end

local function sortedKeys(bucket)
	local keys = {}

	for key in pairs(bucket.counts) do
		table.insert(keys, key)
	end

	table.sort(keys, function(a, b)
		return mw.ustring.lower(bucket.labels[a]) < mw.ustring.lower(bucket.labels[b])
	end)

	return keys
end

local function queryFormLink(frame, game, propertyName, item)
	return frame:preprocess(string.format(
		"{{#queryformlink: form=Resource filter" ..
		"|link text=%s" ..
		"|Resource filter[Related game]=%s" ..
		"|Resource filter[%s]=%s" ..
		"|Resource filter[Has filter]=%s" ..
		"|_run=1" ..
		"}}",
		item,
		game,
		propertyName,
		item,
		propertyName
	))
end

local function renderList(frame, field, bucket, game, showCounts)
	local html = mw.html.create()
	local resItem = html:tag('div'):addClass('d-flex flex-column flex-md-row border border-secondary mb-1')
	
	resItem:tag('div'):addClass('resources-header px-2'):wikitext(field.title):css({
		['flex'] =  '0 1 100px',
		['border-right'] = '1px solid var(--secondary)',
		['background-color'] = 'rgba(255,255,255,.1)'
	})
	local cardBody = resItem:tag('div'):addClass('d-flex flex-wrap px-3'):css({
		['gap'] = '.5rem'
	})
	
	local keys = sortedKeys(bucket)

	if #keys == 0 then
		cardBody:tag('div'):wikitext("None found.")
		return html
	end

	for _, key in ipairs(keys) do
		local label = bucket.labels[key]
		local link = queryFormLink(frame, game, field.property, label)

		if showCounts then
			cardBody:tag('div'):wikitext(link .. " (" .. bucket.counts[key] .. ")")
		else
			cardBody:tag('div'):wikitext(link)
		end
	end

	return html
end

local function askRows(args)
	local game = args.game or "Half-Life"
	local limit = args.limit or "5000"

	return mw.smw.ask({
		"[[Resource:+]]",
		"[[Related game::" .. game .. "]]",
		"?Has resource type=resources",
		"?Has content type=types",
		"?Has language=langs",
		"?Has date#-F[Y]=years",
		"limit=" .. limit,
		"mainlabel=-",
	}) or {}
end

function p.render(frame)
	local parent = frame:getParent()
	local args = setmetatable(frame.args, {
		__index = parent and parent.args or {},
	})

	local game = args.game or "Half-Life"
	local showCounts = args.counts ~= "no"
	local rows = askRows(args)
	local buckets = collect(rows)

	local html = mw.html.create()
	
	local res = html:tag('div')
	res:tag('div'):wikitext('Resources'):addClass('font-weight-bold')
	
	for _, field in ipairs(FIELDS) do
		res:node(renderList(frame, field, buckets[field.key], game, showCounts))
	end

	return html
end

function p.filtered(frame)
	local item   = frame.args['item']
	local parent = frame:getParent()
	local args = setmetatable(frame.args, {
		__index = parent and parent.args or {},
	})

	local game   = args['Related game']
	local filter = args['Has filter']

	local query = frame:callParserFunction("#ask", {
		"[[Resource:+]][[Related game::" .. game .. "]][[" .. filter .. "::~" .. item .. "]]",
		"format=embedded",
		"embedformat=h5",
		"limit=5000",
	})

	return query
end

p.main = p.render

return p