Module:Gallery List

local p = {} local h = {} local Data = mw.loadData("Module:Gallery List/Data")

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

local CATEGORY_INVALID_ARGS = ""

-- Generally the latest remake is the default tab but some remakes don't have enough high quality images uploaded yet local DEFAULT_TAB_DENYLIST = { ["OoT3D"] = true, ["MM3D"] = true, ["TWWHD"] = true, ["TPHD"] = true, ["SSHD"] = true, }

-- Enhanced ports may have the same assets as the original game -- There's no need to show a separate tab in those cases local ENHANCED_PORTS = { ["OoTMQ"] = true, --["FSAE"] = true -- turns out FSAE does have its own sprites }

local doubleDigits = false

function p.Main(frame) local args, err = utilsArg.parse(frame:getParent.args, p.Templates["Gallery List"]) local categories = err and err.categoryText or "" local subjectType = args.subjectType local pagename = mw.title.getCurrentTitle.text

local tabData = {} local defaultTab = 1 for i, game in ipairs(Franchise.enumGames) do		local isListing = utilsString.endsWith(pagename, " in "..Franchise.shortName(game)) local subjects = args[game] subjects = subjects and utilsTable.filter(subjects, utilsString.notEmpty) if subjects and #subjects > 0 then local gallery, subjectTerms = h.printGallery(subjectType, game, subjects, args) table.insert(tabData, {				label = Franchise.display(game),				content = gallery			}) if not DEFAULT_TAB_DENYLIST[game] then defaultTab = #tabData end if not isListing then frame:expandTemplate({					title = "Location Features/Store",					args = {						feature = subjectType,						game = game,						subjects = table.concat(subjectTerms, ", ")					}				}) for j, remake in ipairs(Franchise.remakes(game)) do					if not args[remake] and not ENHANCED_PORTS[remake] then local gallery, subjectTerms = h.printGallery(subjectType, remake, subjects, args) table.insert(tabData, {							label = Franchise.display(remake),							content = gallery,						}) if not DEFAULT_TAB_DENYLIST[remake] then defaultTab = #tabData end frame:expandTemplate({							title = "Location Features/Store",							args = {								feature = subjectType,								game = remake,								subjects = table.concat(subjectTerms, ", ")							}						}) end end end end end local tabs = utilsLayout.tabs(tabData, {		default = defaultTab,		tabOptions = {			collapse = true,		},	}) local html = mw.html.create("div") :addClass("zw-gallery-list") :wikitext(tabs) if doubleDigits then categories = categories.."" end return tostring(html), categories end

function p.entry(game, fileType, subject, options) local options = options or {} local useTerms = options.useTerms local subject, info = utilsMarkup.separateMarkup(subject) local link local term if useTerms ~= false then link = Term.link(subject, game, options) term = Term.fetchTerm(subject, game, options) or subject else link = string.format("%s", subject) term = subject end

local suffix = fileType == "" and "" or " "..fileType local info, variant = p.parseVariant(info) if variant then if tonumber(variant) then suffix = suffix .. " " .. variant else -- numbers can be placed in quotes to escape the above functionality -- e.g. Items in Spirit Tracks uses Quiver ["50"] to produce `File:ST Quiver 50 Icon.png` instead of `File:ST Quiver Icon 50.png` variant = utilsString.trim(variant, ")			suffix = " " .. variant .. suffix		end	end	local entry = {		link = link..info,		term = term,		subject = subject,	}	if variant == "No Image" then		entry.file = "File:No Image.png"	elseif variant and string.find(variant, "^File:") then		entry.file = variant	elseif useTerms == false then		entry.file = string.format("File:%s %s%s.png", game, subject, suffix)	else		term = string.gsub(term, "#", "") -- filenames can't have # so we strip them from the terms		term = string.gsub(term, "", "") -- same goes for any formatting in the term, e.g. Cuccodex''		entry.file = string.format("File:%s %s%s.png", game, term, suffix)	end	return entry end

local VARIANT_REGEX = "^%s*%[([^%]]+)%]" function p.parseVariant(str) local str, markup = utilsMarkup.separateMarkup(str) local variant = string.match(markup, VARIANT_REGEX) markup = string.gsub(markup, VARIANT_REGEX, "") return str..markup, variant end

function h.printGallery(subjectType, game, subjects, options) local entries = h.parseEntries(subjectType, game, subjects, options)

local galleryContent = "" for i, galleryEntry in ipairs(entries) do		galleryContent = galleryContent..galleryEntry.file.."|"..galleryEntry.link.."\n" end local size = Data.sizes[game] and Data.sizes[game][subjectType] or {} local gallery = mw.getCurrentFrame:extensionTag({		name = "gallery",		content = galleryContent,		args = {			caption = options.caption,			widths = options.widths or size.widths,			heights = options.heights or size.heights,			perrow = options.perrow,		}	}) local terms = utilsTable.map(entries, "term") terms = utilsTable.unique(terms)

return gallery, terms end

function h.parseEntries(subjectType, game, subjects, options) local options = options or {} local fileType = options.fileType if not fileType then local graphics = Franchise.graphics(game) if subjectType == "Locations" then fileType = "" elseif graphics == "2D" then fileType = "Sprite" elseif subjectType == "Items" then fileType = "Icon" elseif subjectType == "Treasures" then fileType = "Icon" elseif subjectType == "Zonai Devices" then fileType = "Icon" else fileType = "Model" end end local entries = {} for i, subject in ipairs(subjects) do		local entry = p.entry(game, fileType, subject, options) table.insert(entries, entry) end return entries end

function p.Schemas return { entry = { game = { required = true, type = "string", desc = "A game code from Data:Franchise.", },			fileType = { required = true, type = "string", desc = "A file type such as  or  .", },			subject = { required = true, type = "string", desc = "A wiki article name referring to the subject for which a filename is being generated.", },			options = { type = "record", desc = "The options object is passed along to Module:Term and Module:Term, which is used to create the return object unless  is false.", properties = { {						name = "useTerms", type = "boolean", },				},			},		},		Data = { type = "record", required = true, properties = { {					name = "sizes", desc = "Sets the size of gallery thumbnails based on the  and   parameters of Template:Gallery List.", required = true, type = "map", keyPlaceholder = "game", keys = { type = "string" }, values = { type = "map", keyPlaceholder = "subjectType", keys = { type = "string" }, values = { type = "record", properties = { {									name = "widths", type = "string", required = true, desc = "A value in pixels corresponding to the  ." },								{									name = "heights", type = "string", required = true, desc = "A value in pixels corresponding to the  ." },							},						},					},				},			},		}	} end

function p.Documentation return { entry = { desc = "Allows other modules such as Module:Data Table to use Template:Gallery List's filename generation syntax.", params = {"game", "fileType", "subject", "options"}, returns = "An object containing a filename, a term link, a term string, and the  received minus any Template:Gallery List syntax.", cases = { outputOnly = true, {					args = {"TMC", "Sprite", "Candy (The Minish Cap)"}, expect = { file = "File:TMC Candy Sprite.png", subject = "Candy (The Minish Cap)", term = "Candy", link = "Candy", }				},				{					args = {"OoS", "Sprite", "Ore Chunk [Red] (10) "}, expect = { file = "File:OoS Ore Chunk Red Sprite.png", subject = "Ore Chunk", term = "Ore Chunk", link = "Ore Chunk (10) ", }				},				{					args = {"TMC", "", "Stained Glass "}, expect = { file = "File:TMC Stained Glass Artwork.png", subject = "Stained Glass", term = "Stained Glass", link = "Stained Glass", }				},				{					args = {"TMC", "", "Hyper Pico Bloom [No Image]"}, expect = { file = "File:No Image.png", term = "Hyper Pico Bloom", subject = "Hyper Pico Bloom", link = "Hyper Pico Bloom", }				},				{					args = {"TWW", "Figurine Model", "Deku Tree", { useTerms = false }}, expect = { file = "File:TWW Deku Tree Figurine Model.png", subject = "Deku Tree", term = "Deku Tree", link = "Deku Tree", },				},				{					args = {"ALttP", "30 Sprite", "Bomb", { plural = true } }, expect = { file = "File:ALttP Bombs 30 Sprite.png", subject = "Bomb", term = "Bombs", link = "Bombs", }				},			},		},		parseVariant = { params = {"str"}, returns = { " minus any Template:Gallery List variant syntax", "The variant value, or nil if there is none in .", },			cases = { outputOnly = true, {					args = {"Keese [2]"}, expect = {"Keese", "2"} },				{					args = {"Ore Chunk [Red] (10) "}, expect = {"Ore Chunk (10) ", "Red"}, },				{					args = {"Keese"}, expect = {"Keese", nil} }			}		}	} end

function p.templateData local paramOrder = {1, "fileType", "storeAs", "caption", "perrow", "widths", "heights"} local params = { [1] = {			name = "subjectType", required = true, type = "string", enum = {"Animals", "Characters", "Creatures", "Enemies", "Equipment", "Locations", "Items", "Materials", "Objects", "Treasures", "Zonai Devices"}, desc = "The type of subject being listed.", trim = true, nilIfEmpty = true, },		fileType = { type = "string", desc = "Sets a custom filename suffix for gallery entries", trim = true, },		storeAs = { type = "string", desc = "Stores the list in the Sequences Cargo table for use by Template:Sort Value and Module:Sequences.", canOmit = true, trim = true, nilIfEmpty = true, },		caption = { type = "string", desc = "Caption text for the gallery.", trim = true, nilIfEmpty = true, canOmit= true, },		perrow = { type = "number", desc = "Maximum number of thumbnails to show per row in the gallery.", trim = true, nilIfEmpty = true, canOmit = true, },		widths = { type = "string", desc = "A value in pixels. Sets the width of gallery entries, overriding any default set in Module:Gallery List/Data", trim = true, nilIfEmpty = true, canOmit = true, },		heights = { type = "string", desc = "A value in pixels. Sets the heights of gallery entries, overriding any default set in Module:Gallery List/Data", trim = true, nilIfEmpty = true, canOmit = true, }	}	for i, game in ipairs(Franchise.enumGames) do		if Franchise.isCanon(game) or Franchise.family(game) ~= "" then -- only games featured on the main page for now table.insert(paramOrder, game) params[game] = { type = "string", desc = "Comma-separated list of wiki page names referring to subjects in "..Franchise.display(game), trim = true, split = true, }		end end return { format = "block", params = params, paramOrder = paramOrder, } end

p.Templates = { ["Gallery List"] = p.templateData }

return p