Module:Infobox: Difference between revisions

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search
(Allow br tags in image arguments because those are usually semantically valid (or close enough))
(Remove dependency on variable IsAfterInfobox - using CSS selectors instead due to likely deprecation of Extension:Variables)
 
(One intermediate revision by the same user not shown)
Line 9: Line 9:
local utilsString = require("Module:UtilsString")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")
local utilsTable = require("Module:UtilsTable")
local utilsVar = require("Module:UtilsVar")


local CATEGORY_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CATEGORY_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CATEGORY_PARAM_CAPTION = "[[Category:Infoboxes using captions]]"
local CATEGORY_PARAM_CAPTION = "[[Category:Infoboxes using captions]]"
local CATEGORY_PARAM_NAME = "[[Category:Infoboxes using the name parameter]]"
local CATEGORY_PARAM_NAME = "[[Category:Infoboxes using the name parameter]]"
local CATEGORY_BR_TAGS = "[[Category:Infoboxes using br tags]]"
local CATEGORY_BR_TAGS_GAME = "[[Category:Infoboxes using br tags in game fields]]"
local CLASS_TOOLTIP = require("Module:Constants/class/tooltip")
local CLASS_TOOLTIP = require("Module:Constants/class/tooltip")
local DEFAULT_IMG_SIZE = "320x320px"
local DEFAULT_IMG_SIZE = "320x320px"
local VAR_IS_AFTER_INFOBOX = require("Module:Constants/var/isAfterInfobox")


function p.Main(frame)
function p.Main(frame)
Line 37: Line 33:
if args.caption and args.caption ~= "" then
if args.caption and args.caption ~= "" then
categories = categories..CATEGORY_PARAM_CAPTION
categories = categories..CATEGORY_PARAM_CAPTION
end
for k, v in pairs(args) do
if string.find(v, "<br") and k ~= "image" then
h.warn("It appears the <code>%s</code> field may be using <code><nowiki><br></nowiki></code> tags to create a list. If so, please use [[Template:List]] or [[Template:Infobox Game Blocks]] instead. See [[:Category:Infoboxes using br tags]] for more information.", k)
categories = categories..CATEGORY_BR_TAGS
end
end
end
Line 50: Line 39:
args = { src = "Module:Infobox/Styles.css" }
args = { src = "Module:Infobox/Styles.css" }
})
})
utilsVar.set(VAR_IS_AFTER_INFOBOX, true)
return styles, categories
return styles, categories
Line 92: Line 79:
return nil
return nil
end
end
 
games = utilsString.split(games)
if string.find(games, "<br") then
categories = categories..CATEGORY_BR_TAGS_GAME
games = utilsString.split(games, "<br>")
games = utilsTable.flatMap(games, utilsString._split("<br/>"))
else
games = utilsString.split(games)
end


local gameLinks = utilsTable.map(games, p.link)
local gameLinks = utilsTable.map(games, p.link)
Line 184: Line 164:
categories = categories..CATEGORY_INVALID_ARGS
categories = categories..CATEGORY_INVALID_ARGS
end
end
if listItems and string.find(listItems, "<br") then
if listItems then
h.warn("Using <code><nowiki><br></nowiki></code> tags to create lists is discouraged. Use commas instead, as this template treats them as delimiters. See [[Template:Infobox Game Blocks]] for more information.")
categories = categories..CATEGORY_BR_TAGS
listItems = utilsString.split(listItems, "<br>")
listItems = utilsTable.flatMap(listItems, utilsString._split("<br/>"))
elseif listItems then
listItems = utilsString.split(listItems, '%s*,%f[^,%d]%s*') -- %f[^,%d] is so we don't split numbers on their thousands separator (e.g., 1,500)
listItems = utilsString.split(listItems, '%s*,%f[^,%d]%s*') -- %f[^,%d] is so we don't split numbers on their thousands separator (e.g., 1,500)
end
if listItems then
table.insert(blocks, {
table.insert(blocks, {
game = game,  
game = game,  
Line 385: Line 358:
{
{
args = {"OoT, invalid game, TP"},
args = {"OoT, invalid game, TP"},
},
{
desc = "br tags are discouraged due to the poor HTML semantics.",
args = {"{{OoT}}<br>{{TP}}<br/>{{TotK}}"},
},
},
}
}

Latest revision as of 13:44, 23 May 2024

This is the main module for the following templates: In addition, this module exports the following functions.

Image

{{#invoke:Infobox|Image|<file>||size=}}

Used by infobox templates to generate an image when Template:Media is not used.

Parameters

ParameterStatusDescriptionDefault value
1fileoptionalThe image parameter - a file name.
2captionoptionalThe caption parameter.
sizeoptionalImage size in pixels. Sprites are scaled to a maximum of 10 times their original size.320x320px

Examples

#InputOutputResult
1
{{#invoke:Infobox|Image|File:TWW Great Fairy Figurine Model.png}}
2
{{#invoke:Infobox|Image|File:TWW Great Fairy Figurine Model.png|Great Fairy Figurine|size= 250px}}
Great Fairy Figurine
Sprites are scaled to a maximum of 10 times their original size.
3
{{#invoke:Infobox|Image|File:ALttP Apple Sprite.png}}
Template:Media output is rendered as-is
4
{{#invoke:Infobox|Image|{{Media|Model TWW= TWW Great Fairy Figurine Model.png}}}}
Anything other than a file name starting with File: is rendered as-is
5
{{#invoke:Infobox|Image|{{Plural|Series|Cyber Pico Bloom}} are never seen in-game}}
Cyber Pico Blooms are never seen in-game

Games

{{#invoke:Infobox|Games|<param>}}

Used by infobox templates to turn comma-separated game codes into lists of games.

Parameters

ParameterStatusDescription
1paramoptionalThe infobox parameter, usually {{{game|}}} or {{{other|}}}.

Examples

#InputOutputResult
6
{{#invoke:Infobox|Games|TLoZ, TAoL, ALttP}}
7
{{#invoke:Infobox|Games|TLoZ (Ran), BoMC, TLoZ (Susumu)}}
8
{{#invoke:Infobox|Games|}}
9
{{#invoke:Infobox|Games}}
10
{{#invoke:Infobox|Games|invalid game}}
11
{{#invoke:Infobox|Games|OoT, invalid game, TP}}

local p = {}
local h = {}

local File = require("Module:File")
local Franchise = require("Module:Franchise")
local Term = require("Module:Term")
local utilsArg = require("Module:UtilsArg")
local utilsMarkup = require("Module:UtilsMarkup")
local utilsString = require("Module:UtilsString")
local utilsTable = require("Module:UtilsTable")

local CATEGORY_INVALID_ARGS = "[[Category:"..require("Module:Constants/category/invalidArgs").."]]"
local CATEGORY_PARAM_CAPTION = "[[Category:Infoboxes using captions]]"
local CATEGORY_PARAM_NAME = "[[Category:Infoboxes using the name parameter]]"
local CLASS_TOOLTIP = require("Module:Constants/class/tooltip")
local DEFAULT_IMG_SIZE = "320x320px"

function p.Main(frame)
	local templateName = "Infobox "..frame:getParent():getTitle()
	
	local args, err, categories
	local templateSpec = p.Templates[templateName]
	if templateSpec then
		args, err = utilsArg.parse(frame:getParent().args, templateSpec)
	else
		args = frame:getParent().args
	end
	categories = err and err.categoryText or ""
	
	if args.name and args.name ~= "" then
		categories = categories..CATEGORY_PARAM_NAME
	end
	if args.caption and args.caption ~= "" then
		categories = categories..CATEGORY_PARAM_CAPTION
	end
	
	local styles = frame:extensionTag({
		name = "templatestyles",
		args = { src = "Module:Infobox/Styles.css" }
	})
	
	return styles, categories
end

function p.Image(frame)
	local file = frame.args[1]
	local caption = frame.args[2]
	if file == nil or file == "" then
		return nil
	elseif not utilsString.startsWith(file, "File:") then
		return file
	else
		local image, exists = File.image(file, {
			size = frame.args.size or DEFAULT_IMG_SIZE, -- unclear whether we should even support custom sizing or force them all to 320x320px.
			scale = 10,
		})
		if exists then
			-- Set this image as the article's representative image for things like page previews
			mw.ext.seo.set({
				image = file
			})
		end
		if caption and caption ~= "" then
			local html = mw.html.create("div")
				:addClass("infobox__image-caption")
				:wikitext(caption)
			image = image .. tostring(html)
		end
		return image
	end
end

function p.Games(frame)
	local games = frame.args[1]
	local categories = ""

	games = games and utilsString.trim(games)
	if games == nil or games == "" then
		return nil
	end
	games = utilsString.split(games)

	local gameLinks = utilsTable.map(games, p.link)
	local gameLinks = utilsTable.compact(gameLinks)
	if #gameLinks ~= #games then
		categories = categories..CATEGORY_INVALID_ARGS
	end
	
	if #gameLinks == 1 then
		return gameLinks[1], categories
	else
		local gameList = utilsMarkup.list(gameLinks)
		return gameList, categories
	end
end
function p.link(game)
	if utilsMarkup.containsLink(game) then
		return game
	end
	local game, notes = utilsMarkup.separateMarkup(game)
	local link = Franchise.link(game)
	local properCode = Franchise.code(game)
	if not link then
		h.warn(string.format("Invalid entry <code>%s</code>. See [[Data:Franchise]] for a list of valid entries.", game))
		return nil
	elseif properCode and properCode ~= game then
		h.warn(string.format("<code>%s</code> should be written as <code>%s</code>", game, properCode))
	end
	return link..notes
end

function p.GameBlocks(frame)
	local args = frame:getParent().args
	local blocks, categories = h.parseBlocks(args)
	
	local html = mw.html.create("ul"):addClass("infobox-game-blocks")
	for i, block in ipairs(blocks) do
		local gameText = html
			:tag("li")
			:addClass("infobox-game-blocks__game")
			:tag("span")
				:addClass("infobox-game-blocks__game-text")
		local game = gameText:done()
	
		if blocks.compact then
			gameText:wikitext(Franchise.display(block.game))
			local gameList = game
				:tag("ul")
				:addClass("infobox-game-blocks__game-list")
			for j, listItem in ipairs(block.listItems) do
				gameList
					:tag("li")
					:addClass("infobox-game-blocks__game-list-item")
					:wikitext(listItem)
			end
		else
			html:addClass("infobox-game-blocks--compact")
			gameText
				:tag("span")
					:addClass(CLASS_TOOLTIP)
					:attr("title", Franchise.shortName(block.game))
					:wikitext(block.game)
					:done()
				:wikitext(": ")
			game:wikitext(block.listItems[1])
		end
	end

	return tostring(html), categories
end
function h.parseBlocks(args)
	local categories = ""

	local seenParams = {}
	local blocks = {}
	blocks.compact = false
	for i, game in ipairs(Franchise.enum({ includeSeries = true })) do
		seenParams[game] = true
		local listItems = args[game]
		listItems = listItems and utilsString.trim(listItems)
		listItems = listItems and utilsString.nilIfEmpty(listItems)
		if listItems and string.find(listItems, ", %l") then
			h.warn("Lowercase character detected following comma. Within <code>Template:Infobox Game Blocks</code>, commas are used to separate list items. All list items should be in sentence case. Literal commas can be escaped using {{Template|,}}.")
			categories = categories..CATEGORY_INVALID_ARGS
		end
		if listItems then
			listItems = utilsString.split(listItems, '%s*,%f[^,%d]%s*') -- %f[^,%d] is so we don't split numbers on their thousands separator (e.g., 1,500)
			table.insert(blocks, {
				game = game, 
				listItems = listItems,
			})
		end
		local hasDiv = listItems and listItems[1] and string.find(listItems[1], "<div") -- See [[Minuet of Forest]] "Notes" field, for example
		if listItems and #listItems > 1 or hasDiv then
			blocks.compact = true
		end
		if listItems and listItems[1] and string.find(listItems[1], "plainlist") then
			blocks.compact = true
		end
	end

	for k, v in pairs(args) do
		if not seenParams[k] then
			local errorMessage = string.format("Invalid game <code>%s</code>", k)
			h.warn(errorMessage)
			categories = categories..CATEGORY_INVALID_ARGS
		end
	end
	
	return blocks, categories
end

function p.Title(frame)
	local subpageName = mw.title.getCurrentTitle().subpageText
	local term = Term.fetchTerm(subpageName, "Series")
	return term or utilsString.stripTrailingParentheses(subpageName)
end

function h.warn(msg, ...)
	local utilsError = require("Module:UtilsError")
	local warnMessage = string.format(msg, ...)
	utilsError.warn(warnMessage, {
		includeInstance = false,
	})
end

local templateSpec = function(args)
	local singular = args.singular
	local plural = args.plural
	local params = args.params
	local productionParams = args.productionParams
	local imageSuchAs = args.imageSuchAs

	local spec = {
		format = "block",
		purpose = string.format("[[Guidelines:Articles#Infobox|Infobox]] for [[:Category:%s|%s]].", plural, string.lower(plural)),
		categories = {"Infobox templates"},
		boilerplate = {
			separateRequiredParams = false,	
		},
		paramOrder = {"name", "image", "caption"},
		params = {
			name = {
				desc = "<p>Name to use in the infobox header. Defaults to {{Template|Page Name}}.</p><p>In general, this parameter should be omitted unless the title requires italics.</p>",
				type = "content",
			},
			image = {
				desc = string.format("An image to represent the %s, such as %s.", singular, imageSuchAs),
				type = "wiki-file-name",
			},
			caption = {
				desc = "A caption for the image.",
				type = "content",
			}
		},
	}
	for i, param in ipairs(params or {}) do
		spec.params[param.name] = param
		table.insert(spec.paramOrder, param.name)
	end
	for i, param in ipairs(productionParams or {}) do
		spec.params[param.name] = param
		table.insert(spec.paramOrder, param.name)
	end
	
	return spec
end

local released = {
	name = "released",
	desc = "Release date(s) of the film. Use [[Template:Release]].",
	type = "content",
}

p.Templates = {
	["Infobox Game Blocks"] = {},
	["Infobox Film"] = templateSpec({
		singular = "film",
		plural = "films",
		imageSuchAs = "a release poster or a DVD cover",
		params = {
			{
				name = "director",
				desc = "The director(s) of the film.",
				type = "content",
			},
			{
				name = "producer",
				desc = "The producer(s) of the film.",
				type = "content",
			},
			{
				name = "writer",
				desc = "The writer(s) of the film.",
				type = "content",
			},
			{
				name = "production",
				desc = "The companies producing the film.",
				type = "content",
			},
			{
				name = "distributor",
				desc = "The companies distributing the film.",
				type = "content",
			},
			{
				name = "country",
				desc = "Country or countries of production.",	
			},
			released,
		}
	}),
	["Infobox Television"] = templateSpec({
		singular = "television",
		plural = "television",
		imageSuchAs = "the series' logo or title card",
		params = {
			{
				name = "basedOn",
				type = "string",
				desc = "Comma separated list of [[Data:Franchise|games]] that the series is based on.",
				enum = Franchise.enum(),
				trim = true,
				split = true,
			},
			{
				name = "seasons",
				type = "content",
				desc = "Number of seasons.",
			},
			{
				name = "episodes",
				type = "content",
				desc = "Number of total episodes.",
			},
			{
				name = "company",
				type = "content",
				desc = "Production comapany or companies.",
			},
			{
				name = "distributor",
				type = "content",
				desc = "Distributor(s) of the television series.",
			},
			released,
		}
	})
}

function p.Documentation()
	return {
		Games = {
			desc = "Used by [[:Category:Infobox templates|infobox templates]] to turn comma-separated [[Data:Franchise|game codes]] into lists of games.",
			frameParams = {
				[1] = {
					name = "param",
					desc = "The infobox parameter, usually <code><nowiki>{{{game|}}}</nowiki></code> or <code><nowiki>{{{other|}}}</nowiki></code>.",
				},
			},
			cases = {
				{
					args = {"TLoZ, TAoL, ALttP"},
				},
				{
					args = {"TLoZ (Ran), BoMC, TLoZ (Susumu)"},
				},
				{
					args = {""},
				},
				{
					args = {},
				},
				{
					args = {"invalid game"},
				},
				{
					args = {"OoT, invalid game, TP"},
				},
			}
		},
		Image = {
			desc = "Used by [[:Category:Infobox templates|infobox templates]] to generate an image when [[Template:Media]] is not used.",
			frameParams = {
				[1] = {
					name = "file",
					desc = "The image parameter - a file name.",
				},
				[2] = {
					name = "caption",
					desc = "The caption parameter.",
				},
				size = {
					desc = "Image size in pixels. Sprites are scaled to a maximum of 10 times their original size.",
					default = DEFAULT_IMG_SIZE,
				},
			},
			cases = {
				{
					args = {"File:TWW Great Fairy Figurine Model.png"},
				},
				{
					args = {"File:TWW Great Fairy Figurine Model.png", "Great Fairy Figurine", size = "250px"}
				},
				{
					desc = "Sprites are scaled to a maximum of 10 times their original size.",
					args = {"File:ALttP Apple Sprite.png"},
				},
				{
					desc = "[[Template:Media]] output is rendered as-is",
					args = {"{{Media|Model TWW= TWW Great Fairy Figurine Model.png}}"}
				},
				{
					desc = "Anything other than a file name starting with <code>File:</code> is rendered as-is",
					args = {"{{Plural|Series|Cyber Pico Bloom}} are never seen in-game"},
				},
			},
		},
	}
end

return p