Module:Media

From Zelda Wiki, the Zelda encyclopedia
Jump to navigation Jump to search

-- This modules serves as the code for Template:Media.
local p = {}
local h = {}

local File = require("Module:File")
local Franchise = require("Module:Franchise")
local utilsLayout = require("Module:UtilsLayout")
local utilsMarkup = require("Module:UtilsMarkup")
local utilsTable = require("Module:UtilsTable")

local CAT_INVALID_ARGS = require("Module:Constants/category/invalidArgs")

-- In the order in which they are to be presented, from left to right
local mediaTypes = {
	{
		type = "Artwork",
		category = "Artwork",
		description = "Official artwork provided by Nintendo",
	},
	{
		type = "Render",
		category = "Renders",
		description =  "Official models provided by Nintendo",
	},
	{
		type = "Model",
		category = "Models",
		description = "In-game models",
	},
	{
		type = "Sprite",
		category = "Sprites",
		description = "In-game sprites or icons",
	},
	{
		type = "Screenshot",
		category = "Screenshots",
		description = "In-game screenshots",
	},
	{
		type = "Map",
		category = "Maps",
		description = "In-game map of an area",
	},
}

-- Template:Media
function p.Main(frame)
	local args = frame:getParent().args
	return p.main(args)
end

function p.main(args)
	local remainingArgs = utilsTable.clone(args)
	local categories = {}
	local games = {}
	local typeTabs = {}
	local typeTabWithLatestRelease = 1
	local latestReleaseOverall = ""
	local fileCount = 0
	local latestImagePerType = {}
	
	for _, mediaType in ipairs(mediaTypes) do
		local gameTabs = {}
		local defaultGameTab = 1
		local latestReleaseForType = ""
		
		for i, game in ipairs(Franchise.enum({ includeSeries = true, includeGroups = true })) do
			local key = mediaType.type .. " " .. game
			remainingArgs[key] = nil
			
			local file = args[key]
			if file ~= nil and file ~= "" then
				fileCount = fileCount + 1
				table.insert(games, game)
				local img, exists
				if utilsMarkup.containsLink(file) or file ~= mw.text.killMarkers(file) then -- if user specified markup, simply render the markup as-is.
					img = file
					exists = true
				else
					if not string.find(file, "^File:") then
						h.warn(string.format("Invalid filename <code>%s</code>. Filenames must start with <code>File:</code>.", file))
						table.insert(categories, CAT_INVALID_ARGS)
						file = "File:"..file
					end
					img, exists = File.image(file, {
						size = "320x320px",
						scale = mediaType.type == "Sprite" and 10 or nil, -- For sprites, scale to a maximum of 10x its original size. Only done for sprites due to scaling being somewhat expensive.
						isPixelArt = mediaType.type == "Sprite" or Franchise.graphics(game) == "2D"
					})
				end
				table.insert(gameTabs, {
					label = game,
					content = img,
					tooltip = Franchise.shortName(game)
				})
				if not exists and mw.title.getCurrentTitle().nsText ~= "User" then
					table.insert(categories, "Articles lacking files")
					table.insert(categories, "Articles lacking " .. string.lower(mediaType.category))
					table.insert(categories, string.format("%s articles lacking files", Franchise.shortName(game)))
				end
				local release = Franchise.releaseDate(game)
				-- empty string here means an unreleased game. This ensures the unreleased is sorted last.
				-- the 'i' is appended to ensure that multiple unreleased games are sorted in canon order.
				if release == "" then
					 release = "unreleased"..i
				end
				if exists and release and release > latestReleaseForType then
					latestReleaseForType = release
					defaultGameTab = #gameTabs
					latestImagePerType[#typeTabs + 1] = file
				end
				if exists and release and release > latestReleaseOverall then
					latestReleaseOverall = release
					typeTabWithLatestRelease = #typeTabs + 1
				end
			end
		end
		if #gameTabs > 0 then
			table.insert(typeTabs, {
				label = mediaType.type,
				tooltip = mediaType.description,
				gameTabs = gameTabs,
				defaultGameTab = defaultGameTab,
			})
		end
	end
	
	remainingArgs["defaultTypeTab"] = nil
	local defaultTypeTab = utilsTable.findIndex(typeTabs, function(typeTab)
		return typeTab.label == args.defaultTypeTab
	end)
	defaultTypeTab = defaultTypeTab or typeTabWithLatestRelease

	local invalidKeys = utilsTable.keys(remainingArgs)
	if #invalidKeys > 0 then
		local msg = string.format("The following keys use an invalid media type or game code: <code>%s</code><p>For a list of valid types, see [[Template:Media]]. For a list of valid games, see [[Module:Franchise]].</p>", utilsTable.print(invalidKeys))
		h.warn(msg)
		table.insert(categories, CAT_INVALID_ARGS)
	end
	
	local mainGames, otherGames = utilsTable.partition(games, function(game)
		local gameType = Franchise.type(game)
		return gameType == "main" or gameType == "remake"
	end)
	if #mainGames > 0 and #otherGames > 0 then 
		otherGames = utilsTable.map(otherGames, utilsMarkup.code)
		otherGames = mw.text.listToText(otherGames)
		h.warn("Entries for %s should be moved from the infobox to the Gallery section. As per [[Template:Media#Guidelines]], infoboxes should not mix main series games with other games.", otherGames)
		table.insert(categories, CAT_INVALID_ARGS)
	end
	
	local content
	if #typeTabs == 1 and #typeTabs[1].gameTabs == 1 then
		content = typeTabs[1].gameTabs[1].content
	else
		for i, typeTab in ipairs(typeTabs) do
			typeTab.content = utilsLayout.tabs(typeTab.gameTabs, {
				default = typeTab.defaultGameTab,
				align = "center",
				tabOptions = {
					position = "bottom",
					columns = 5,
				},
				contentOptions = {
					alignVertical = "center",
					fixedHeight = true,
				}
			})
		end
		content = utilsLayout.tabs(typeTabs, {
			default = defaultTypeTab,
			align = "center",
			tabOptions = {
				columns = 4,
			},
		})
	end
	
	local pageImage = latestImagePerType[defaultTypeTab]
	if pageImage then
		-- Set the file under the default type and game tab as the article's representative image for page previews
		mw.ext.seo.set({
			image = pageImage
		})
	end
	
	return content .. utilsMarkup.categories(categories)
end

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

return p