Jump to content

Module:Card: Difference between revisions

From The Petit Planet Wiki
why upper here? make it lower instead.
No edit summary
Line 1: Line 1:
local p = {}
local p = {}
local lib = require('Module:Feature')
local lib = require('Module:Feature')
local ne = lib.isNotEmpty
local ne = lib.isNotEmpty
local namespace = require('Module:Namespace_detect')._main
local TemplateData = require('Module:TemplateData')
local getTemplateArgs = require('Module:Parser').getTemplateArgs
local array = require('Module:Array')
local array = require('Module:Array')


local allData = {
local allData = {
--{'Neighbor', mw.loadData('Module:CentralData/Neighbors')},
-- {'Neighbor', mw.loadData('Module:CentralData/Neighbors')},
{'Creature', mw.loadData('Module:CentralData/Creatures')},
{'Creature', mw.loadData('Module:CentralData/Creatures')},
--{'Furniture', mw.loadData('Module:CentralData/Furniture')},
-- {'Furniture', mw.loadData('Module:CentralData/Furniture')},
{'Item', mw.loadData('Module:CentralData/Items')},
{'Item', mw.loadData('Module:CentralData/Items')},
}
}


local COLON = ':'
-- Minimal TemplateData args.
-- This does not need to be huge unless you want full documentation for every parameter.
local CARD_ARGS = {
{
name = 'name',
alias = 1,
default = 'Unknown',
},
{
name = 'label',
alias = {2, 'text', 'count', 'amount', 'x'},
},
{
name = 'show_label',
alias = 'show_text',
type = '1',
},
{
name = 'quality',
alias = 'rarity',
default = 'Gray',
},
{
name = 'type',
default = 'Item',
},
{
name = 'suffix',
default = '',
},
{
name = 'image',
},
{
name = 'image_size',
alias = 'imageSize',
},
{
name = 'image_ext',
alias = {'imageExt', 'extension'},
default = 'png',
},
{
name = 'link',
},
{
name = 'link_suffix',
default = '',
},
{
name = 'nolink',
type = '1',
},
{
name = 'caption',
},
{
name = 'show_caption',
type = '1',
},
{
name = 'caption_width',
alias = 'captionWidth',
},
{
name = 'mini',
type = '1',
},
{
name = 'first',
type = '1',
},
{
name = 'up',
alias = 'UP',
type = '1',
},
{
name = 'galaxy',
},
{
name = 'notice',
},
}


function p.main(frame)
function p.main(frame)
Line 20: Line 103:
parentOnly = false
parentOnly = false
})
})
return p._main(args)
return p._main(args)
end
end


function p._main(args)
function p.getTypeAndData(name, requestedType)
local item = args[1] or args.name or 'Unknown'
local img_type = requestedType or 'Item'
local label = args[2] or args.label or args.text or args.count or args.amount or args.x or nil
local img_data = nil
 
-- Reset agent-specific data to prevent carry-over from previous cards
for _, v in ipairs(allData) do
local nbQuality, nbGalaxy, crQuality, crGalaxy, outQuality, notice = nil, nil, nil, nil, nil, nil
local dataType = v[1]
local dataTable = v[2]
-- Caption Params:
 
local caption = args.caption or nil
if lib.isEmpty(requestedType) or dataType == requestedType then
local caption_width = args.caption_width or args.captionWidth or nil
local data = dataTable[name]
 
    -- Image File Params:
if data then
local type, data = p.getTypeAndData(item, args.type)
img_type = dataType
local suffix = args.suffix or ''
img_data = data
local imageSize = args.image_size or args.imageSize or lib.ternary(args.mini == '1', 48, 70)
break
local imageExt = args.image_ext or args.imageExt or 'png'
end
-- Link Params:
local link = args.link or lib.ternary(item == 'Unknown', '', item)
local link_suffix = args.link_suffix or ''
-- Rank Param:
local quality = args.quality or args.rarity or nil
-- Try to get Rank From Data
if lib.isEmpty(quality) and data then
if data.quality then
quality = data.quality
end
end
end
end
 
-- If quality is still empty, set it to "Gray"
return img_type, img_data
if lib.isEmpty(quality) then
end
quality = 'Gray'  
 
end
local function normalizeBool(value)
return value == true or value == '1' or value == 1 or value == 'yes'
-- If quality contains a semicolon, then split and get first
end
if string.find(quality, ";") then
 
local function normalizeClassName(value)
return tostring(value or '')
:lower()
:gsub('%s+', '-')
end
 
local function normalizeQuality(quality)
quality = tostring(quality or '')
 
if quality:find(';', 1, true) then
quality = array._main({
quality = array._main({
quality,
quality,
Line 67: Line 149:
})
})
end
end
 
-- Make sure quality is lowercase
if lib.isEmpty(quality) then
quality = quality:lower()
quality = 'Gray'
end
-- Modifiers:
 
local isFirst = args.first or false
return normalizeClassName(quality)
local isUp = args.UP or args.up or false
end
 
-- Attribute/galaxy
local function setCaptionWidth(node, width)
local galaxy = args.galaxy or nil
if width == 'auto' or lib.isEmpty(width) then
node:addClass('auto-width')
if lib.isEmpty(suffix) then
else
suffix = ' Icon'
node:css('width', width)
local lcType = type:lower()
end
if (lcType == 'neighbor') then
end
--Grabs Neighbor Quality and Galaxy from Data to display icons
 
nbQuality = data['quality']
function p._processArgs(args)
nbGalaxy = data['galaxy']
local out = TemplateData.processArgs(args, CARD_ARGS)
 
--In Full-Size Card-Mode, if there is no specified text, Agents will default to using their full name displayed underneath.
-- Basic identity
if args.mini ~= "1" then
out.name = out.name or 'Unknown'
if (label == nil) then label = item end
 
-- Data lookup
out.type, out.data = p.getTypeAndData(out.name, out.type)
 
-- Boolean-ish params
out.mini = normalizeBool(out.mini)
out.first = normalizeBool(out.first)
out.up = normalizeBool(out.up)
out.nolink = normalizeBool(out.nolink)
out.show_label = normalizeBool(out.show_label)
out.show_caption = normalizeBool(out.show_caption)
 
-- Image defaults
out.suffix = out.suffix or ''
out.image_ext = out.image_ext or 'png'
 
if lib.isEmpty(out.image_size) then
out.image_size = out.mini and 48 or 70
end
 
if lib.isEmpty(out.suffix) then
out.suffix = ' Icon'
end
 
-- Data-driven name
out.data_name = out.data and out.data.name or nil
 
-- Quality / rarity
if lib.isEmpty(out.quality) and out.data and out.data.quality then
out.quality = out.data.quality
end
 
out.quality = normalizeQuality(out.quality)
 
-- Galaxy
if lib.isEmpty(out.galaxy) and out.data and out.data.galaxy then
out.galaxy = out.data.galaxy
end
 
-- Label/text handling
if out.show_label then
out.label = out.data_name or out.name
end
 
if out.label == '0' then
out.label = nil
end
 
-- Type-specific behavior
local lcType = tostring(out.type or ''):lower()
 
if lcType == 'neighbor' then
if lib.isEmpty(out.label) then
if out.mini and out.data and out.data.shortName then
out.label = out.data.shortName
elseif not out.mini then
out.label = out.name
end
end
-- In Mini-Size Card-Mode, if there is no specified text, Agents will use their "shortName" pulled from /data as the displayed text.
-- In cases where Agents do not have a "shortName", their full name will be displayed instead.
if args.mini == "1" then
if (label == nil) and data['shortName'] ~= nil then label = data['shortName']
elseif (label == nil) and data['shortName'] == nil then label = item
end
end
-- hackish workaround to account for the slight transparent borders on hoyolab agent icons
if (tonumber(imageSize) ~= nil) then
imageSize = tonumber(imageSize) + 2
end
elseif (lcType == 'creature') then
--Grabs Creature Quality and Galaxy from Data to display icons
crQuality = data['quality']
crGalaxy = data['galaxy']
elseif (lcType == 'clothing') then
--Grabs Outfit Quality from Data to display icons
outQuality = data['quality']
end
end
-- Account for transparent borders on neighbor icons
if tonumber(out.image_size) then
out.image_size = tonumber(out.image_size) + 2
end
end
-- Caption handling
if out.show_caption and lib.isEmpty(out.caption) then
out.caption = out.data_name or out.name
end
-- Link handling
if out.link == nil then
out.link = lib.ternary(out.name == 'Unknown', '', out.name)
end
out.link_suffix = out.link_suffix or ''
out.final_link = out.link .. out.link_suffix
if out.nolink then
out.final_link = ''
end
-- File name
if ne(out.image) then
out.final_file = out.image
else
out.final_file = lib.sanitizeFileName(out.name) .. out.suffix .. '.' .. out.image_ext
end
end


--local final_file = args.image or type .. ' ' .. lib.sanitizeFileName(item) .. suffix .. '.' .. imageExt
-- Main image wikitext
local final_file = lib.sanitizeFileName(item) .. suffix .. '.' .. imageExt
local linkPart = out.nolink and '|link=' or ('|link=' .. out.final_link)
 
local final_link = link .. link_suffix
out.image_wikitext = table.concat({
'[[File:',
local file = '[[File:' .. final_file .. '|' .. imageSize .. 'px|alt=' .. item .. '|link=' .. final_link .. ']]'
out.final_file,
'|',
local result = mw.html.create()
out.image_size,
'px|alt=',
local card_container = result:tag('div')
out.name,
linkPart,
']]'
})
 
return out
end
 
function p._main(args)
local a = p._processArgs(args)
 
local node_container = mw.html.create('div')
:addClass('card-container')
:addClass('card-container')
:addClass('card-quality-' .. quality)
 
if a.mini then
if args.mini == "1" then
node_container:addClass('mini-card')
card_container:addClass('mini-card')
end
end
 
local card_wrapper = card_container:tag('div')
-- Old-style wrapper/body structure.
-- This is usually important if your existing CSS was written for the old Card module.
local node_card = node_container:tag('span')
:addClass('card-wrapper')
:addClass('card-wrapper')
:tag('span')
local card_body = card_wrapper:tag('div')
:addClass('card-body')
:addClass('card-body')
 
-- Main image container
local card_link = card_wrapper:tag('div')
node_card:tag('span')
:addClass('card-link')
:wikitext('[[' .. final_link .. ']]')
local card_image = card_body:tag('div')
:addClass('card-image-container')
:addClass('card-image-container')
:wikitext(file)
:addClass('card-quality-' .. a.quality)
:wikitext(a.image_wikitext)
 
-- Galaxy icon
if not a.mini and ne(a.galaxy) then
node_card:addClass('card-galaxy-' .. normalizeClassName(a.galaxy))
 
node_card:tag('span')
:addClass('card-icon-galaxy')
:wikitext(
'[[File:Icon ' ..
a.galaxy ..
'.png|alt=' ..
a.galaxy ..
'|link=' ..
a.galaxy ..
'|20px]]'
)
end
 
-- Notice label
if not a.mini and ne(a.notice) then
node_card:tag('span')
:addClass('card-notice-label')
:wikitext(a.notice)
end
 
-- First modifier
if a.first then
node_card:tag('span')
:addClass('card-modifier')
:addClass('card-modifier-first')
:wikitext('[[File:Icon Item First.png|x25px|link=|alt=First]]')
end


--[[if label and not (args.label == "0" or args.text == "0") then --]]
-- UP modifier
    if (label or args.show_text) and (args.label ~= "0" or args.text ~= "0") then
if a.up then
if args.show_label == "1" or args.show_text == "1" then
node_card:tag('span')
label = data and data.name or item
:addClass('card-modifier')
end
:addClass('card-modifier-up')
:wikitext('[[File:Icon Item UP.png|x25px|link=|alt=UP]]')
local card_label = card_container:tag('div')
end
 
-- Label/text below card image
if ne(a.label) then
node_container:tag('span')
:addClass('card-label')
:addClass('card-label')
:wikitext(label)
:wikitext(a.label)
end
end


if (caption or args.show_caption) and args.show_caption ~= "0" then
-- Caption below card
if args.show_caption == "1" then
if a.show_caption and ne(a.caption) then
caption = (data and data.name) or item
local node_caption = node_container:tag('span')
end
local card_caption = card_container:tag('div')
:addClass('card-caption')
:addClass('card-caption')
:wikitext('[[', final_link, '|', caption, ']]')
 
if a.nolink or lib.isEmpty(a.final_link) then
if caption_width == 'auto' or lib.isEmpty(caption_width) then
node_caption:wikitext(a.caption)
card_caption:addClass('auto-width')
else
else
card_caption:css('width', caption_width)
node_caption:wikitext('[[', a.final_link, '|', a.caption, ']]')
end
end
setCaptionWidth(node_caption, a.caption_width)
end
end
 
if (isFirst) then
return tostring(node_container)
local card_mod_first = card_wrapper:tag('div')
end
:addClass('card-modifier card-modifier-first')
 
:wikitext('[[File:Icon Item First.png|x25px]]')
function p.templateData(frame)
end
return TemplateData.templateData(CARD_ARGS, {
description = 'Creates an item-style card with image, quality, label, caption, modifiers, and optional data-driven icons.',
if (isUp) then
format = 'inline'
local card_mod_up = card_wrapper:tag('div')
}, frame)
:addClass('card-modifier card-modifier-up')
:wikitext('[[File:Icon Item UP.png|x25px]]')
end
-- Attribute icon, applied automatically for agents or manually specified for anything else
if args.mini ~= "1" then  -- Only display the icons if mini is not set to "1"
if ne(nbGalaxy) then
card_body:addClass('card-galaxy-' .. string.lower(nbGalaxy))
card_body:tag('span')
:addClass('card-icon-galaxy')
:wikitext('[[File:Icon ', nbGalaxy, '.png|alt=', nbGalaxy, '|link=', nbGalaxy, '|20px]]')
end
if ne(crGalaxy) then
card_body:addClass('card-galaxy-' .. string.lower(crGalaxy))
card_body:tag('span')
:addClass('card-icon-galaxy')
:wikitext('[[File:Icon ', crGalaxy, '.png|alt=', crGalaxy, '|link=', crGalaxy, '|20px]]')
end
    local notice = args.notice or nil
    if ne(notice) then
    card_body:tag('span')
:addClass('card-notice-label')
:wikitext(notice)
    end
end
return tostring(result)
end
end


function p.getTypeAndData(name, type)
function p.syntax(frame)
local img_type = type or 'Item'
return TemplateData.syntax(CARD_ARGS, frame)
local img_data = nil
for i, v in ipairs(allData) do
-- Only if the type was either not provided in the card args or if the provided type from card args is equal
if lib.isEmpty(type) or v[1] == type then
local data = v[2][name]
if data then
img_type = v[1]
img_data = data
break
end
end
end
return img_type, img_data
end
end


return p
return p

Revision as of 16:50, 2 May 2026

Module documentation
This Lua module is used primarily on pages in the Main namespace. It can be found on 2,429 pages, or on about 60.62% of those pages.
Edits to this Lua module will be widely noticed. To avoid large-scale disruption and unnecessary server load, any changes to this Lua module should be tested in its sandbox or testcases subpages. The tested changes can then be added to this page in one single edit.
Please consider discussing any changes in the wiki's Discord.

This module implements {{Card}}.

To update, see: Module:CentralData.

local p = {}

local lib = require('Module:Feature')
local ne = lib.isNotEmpty
local TemplateData = require('Module:TemplateData')
local array = require('Module:Array')

local allData = {
	-- {'Neighbor', mw.loadData('Module:CentralData/Neighbors')},
	{'Creature', mw.loadData('Module:CentralData/Creatures')},
	-- {'Furniture', mw.loadData('Module:CentralData/Furniture')},
	{'Item', mw.loadData('Module:CentralData/Items')},
}

-- Minimal TemplateData args.
-- This does not need to be huge unless you want full documentation for every parameter.
local CARD_ARGS = {
	{
		name = 'name',
		alias = 1,
		default = 'Unknown',
	},
	{
		name = 'label',
		alias = {2, 'text', 'count', 'amount', 'x'},
	},
	{
		name = 'show_label',
		alias = 'show_text',
		type = '1',
	},
	{
		name = 'quality',
		alias = 'rarity',
		default = 'Gray',
	},
	{
		name = 'type',
		default = 'Item',
	},
	{
		name = 'suffix',
		default = '',
	},
	{
		name = 'image',
	},
	{
		name = 'image_size',
		alias = 'imageSize',
	},
	{
		name = 'image_ext',
		alias = {'imageExt', 'extension'},
		default = 'png',
	},
	{
		name = 'link',
	},
	{
		name = 'link_suffix',
		default = '',
	},
	{
		name = 'nolink',
		type = '1',
	},
	{
		name = 'caption',
	},
	{
		name = 'show_caption',
		type = '1',
	},
	{
		name = 'caption_width',
		alias = 'captionWidth',
	},
	{
		name = 'mini',
		type = '1',
	},
	{
		name = 'first',
		type = '1',
	},
	{
		name = 'up',
		alias = 'UP',
		type = '1',
	},
	{
		name = 'galaxy',
	},
	{
		name = 'notice',
	},
}

function p.main(frame)
	local args = require('Module:Arguments').getArgs(frame, {
		wrapper = { 'Template:Card' },
		parentOnly = false
	})

	return p._main(args)
end

function p.getTypeAndData(name, requestedType)
	local img_type = requestedType or 'Item'
	local img_data = nil

	for _, v in ipairs(allData) do
		local dataType = v[1]
		local dataTable = v[2]

		if lib.isEmpty(requestedType) or dataType == requestedType then
			local data = dataTable[name]

			if data then
				img_type = dataType
				img_data = data
				break
			end
		end
	end

	return img_type, img_data
end

local function normalizeBool(value)
	return value == true or value == '1' or value == 1 or value == 'yes'
end

local function normalizeClassName(value)
	return tostring(value or '')
		:lower()
		:gsub('%s+', '-')
end

local function normalizeQuality(quality)
	quality = tostring(quality or '')

	if quality:find(';', 1, true) then
		quality = array._main({
			quality,
			';',
			firstItemOnly = 1
		})
	end

	if lib.isEmpty(quality) then
		quality = 'Gray'
	end

	return normalizeClassName(quality)
end

local function setCaptionWidth(node, width)
	if width == 'auto' or lib.isEmpty(width) then
		node:addClass('auto-width')
	else
		node:css('width', width)
	end
end

function p._processArgs(args)
	local out = TemplateData.processArgs(args, CARD_ARGS)

	-- Basic identity
	out.name = out.name or 'Unknown'

	-- Data lookup
	out.type, out.data = p.getTypeAndData(out.name, out.type)

	-- Boolean-ish params
	out.mini = normalizeBool(out.mini)
	out.first = normalizeBool(out.first)
	out.up = normalizeBool(out.up)
	out.nolink = normalizeBool(out.nolink)
	out.show_label = normalizeBool(out.show_label)
	out.show_caption = normalizeBool(out.show_caption)

	-- Image defaults
	out.suffix = out.suffix or ''
	out.image_ext = out.image_ext or 'png'

	if lib.isEmpty(out.image_size) then
		out.image_size = out.mini and 48 or 70
	end

	if lib.isEmpty(out.suffix) then
		out.suffix = ' Icon'
	end

	-- Data-driven name
	out.data_name = out.data and out.data.name or nil

	-- Quality / rarity
	if lib.isEmpty(out.quality) and out.data and out.data.quality then
		out.quality = out.data.quality
	end

	out.quality = normalizeQuality(out.quality)

	-- Galaxy
	if lib.isEmpty(out.galaxy) and out.data and out.data.galaxy then
		out.galaxy = out.data.galaxy
	end

	-- Label/text handling
	if out.show_label then
		out.label = out.data_name or out.name
	end

	if out.label == '0' then
		out.label = nil
	end

	-- Type-specific behavior
	local lcType = tostring(out.type or ''):lower()

	if lcType == 'neighbor' then
		if lib.isEmpty(out.label) then
			if out.mini and out.data and out.data.shortName then
				out.label = out.data.shortName
			elseif not out.mini then
				out.label = out.name
			end
		end

		-- Account for transparent borders on neighbor icons
		if tonumber(out.image_size) then
			out.image_size = tonumber(out.image_size) + 2
		end
	end

	-- Caption handling
	if out.show_caption and lib.isEmpty(out.caption) then
		out.caption = out.data_name or out.name
	end

	-- Link handling
	if out.link == nil then
		out.link = lib.ternary(out.name == 'Unknown', '', out.name)
	end

	out.link_suffix = out.link_suffix or ''
	out.final_link = out.link .. out.link_suffix

	if out.nolink then
		out.final_link = ''
	end

	-- File name
	if ne(out.image) then
		out.final_file = out.image
	else
		out.final_file = lib.sanitizeFileName(out.name) .. out.suffix .. '.' .. out.image_ext
	end

	-- Main image wikitext
	local linkPart = out.nolink and '|link=' or ('|link=' .. out.final_link)

	out.image_wikitext = table.concat({
		'[[File:',
		out.final_file,
		'|',
		out.image_size,
		'px|alt=',
		out.name,
		linkPart,
		']]'
	})

	return out
end

function p._main(args)
	local a = p._processArgs(args)

	local node_container = mw.html.create('div')
		:addClass('card-container')

	if a.mini then
		node_container:addClass('mini-card')
	end

	-- Old-style wrapper/body structure.
	-- This is usually important if your existing CSS was written for the old Card module.
	local node_card = node_container:tag('span')
		:addClass('card-wrapper')
		:tag('span')
			:addClass('card-body')

	-- Main image container
	node_card:tag('span')
		:addClass('card-image-container')
		:addClass('card-quality-' .. a.quality)
		:wikitext(a.image_wikitext)

	-- Galaxy icon
	if not a.mini and ne(a.galaxy) then
		node_card:addClass('card-galaxy-' .. normalizeClassName(a.galaxy))

		node_card:tag('span')
			:addClass('card-icon-galaxy')
			:wikitext(
				'[[File:Icon ' ..
				a.galaxy ..
				'.png|alt=' ..
				a.galaxy ..
				'|link=' ..
				a.galaxy ..
				'|20px]]'
			)
	end

	-- Notice label
	if not a.mini and ne(a.notice) then
		node_card:tag('span')
			:addClass('card-notice-label')
			:wikitext(a.notice)
	end

	-- First modifier
	if a.first then
		node_card:tag('span')
			:addClass('card-modifier')
			:addClass('card-modifier-first')
			:wikitext('[[File:Icon Item First.png|x25px|link=|alt=First]]')
	end

	-- UP modifier
	if a.up then
		node_card:tag('span')
			:addClass('card-modifier')
			:addClass('card-modifier-up')
			:wikitext('[[File:Icon Item UP.png|x25px|link=|alt=UP]]')
	end

	-- Label/text below card image
	if ne(a.label) then
		node_container:tag('span')
			:addClass('card-label')
			:wikitext(a.label)
	end

	-- Caption below card
	if a.show_caption and ne(a.caption) then
		local node_caption = node_container:tag('span')
			:addClass('card-caption')

		if a.nolink or lib.isEmpty(a.final_link) then
			node_caption:wikitext(a.caption)
		else
			node_caption:wikitext('[[', a.final_link, '|', a.caption, ']]')
		end

		setCaptionWidth(node_caption, a.caption_width)
	end

	return tostring(node_container)
end

function p.templateData(frame)
	return TemplateData.templateData(CARD_ARGS, {
		description = 'Creates an item-style card with image, quality, label, caption, modifiers, and optional data-driven icons.',
		format = 'inline'
	}, frame)
end

function p.syntax(frame)
	return TemplateData.syntax(CARD_ARGS, frame)
end

return p