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 <br> tag detection - Now that we're purged br lists from the wiki the detection will generate too many false positives to be useful. Better to enforce with editor guidelines and patrolling)
Line 14: Line 14:
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"
Line 37: Line 35:
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 92: Line 83:
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 168:
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 362:
{
{
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}}"},
},
},
}
}

Revision as of 13:48, 22 May 2024

Lua error in package.lua at line 80: module 'Module:Constants/var/isAfterInfobox' not found.


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 utilsVar = require("Module:UtilsVar")

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"
local VAR_IS_AFTER_INFOBOX = require("Module:Constants/var/isAfterInfobox")

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" }
	})

	utilsVar.set(VAR_IS_AFTER_INFOBOX, true)
	
	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