Documentation for this module may be created at Module:File/doc
local p = {} local h = {} local Franchise = require("Module:Franchise") local utilsCargo = require("Module:UtilsCargo") local utilsMarkup = require("Module:UtilsMarkup") local utilsPage = require("Module:UtilsPage") local utilsString = require("Module:UtilsString") local utilsTable = require("Module:UtilsTable") local CARGO_TABLE = "Files" -- Various templates function p.Icon(frame) local args = frame.args local img = p.icon(args[1], args[2], { size = args.size, }) return img end -- Utilities function p.image(filename, options) filename = utilsPage.stripNamespace(filename, "File") options = options or {} local sizeWidth, sizeHeight = p.dimensions(options.size) local checkExists = options.checkExists ~= false -- If the file is a redirect and we are doing an existence check or getting width/height from Cargo, then we need to get the redirectTarget. -- This is a somewhat expensive operation to do at scale so we only do it when needed local originalFilename = filename if checkExists or options.scale then local redirectTarget = mw.title.new("File:"..filename).redirectTarget redirectTarget = redirectTarget and redirectTarget.text if redirectTarget then filename = redirectTarget end end if checkExists and not utilsPage.exists("File:" .. filename, true) then -- We check again with mw.title To work around a bug where moved files are marked non-existant until null-edited -- This happens because of how we use Cargo to query for existence to save on expensive parser functions local title = mw.title.new(filename, "File") if not title.fileExists then return h.noimage(filename, sizeWidth, sizeHeight, options), false end end if options.scale then local file if options.scaleUsingCargo then results = utilsCargo.query(CARGO_TABLE, "width, height", { where = utilsCargo.allOf({ ["_pageName"] = "File:"..filename, }), limit = 1, }) file = results[1] end if not file or utilsString.isEmpty(file.width) or utilsString.isEmpty(file.height) then -- if scaleUsingCargo = false or data does not return from Cargo query for some reason, then use the title object (an expensive parser function) file = mw.title.new("File:"..filename).file end local width = math.floor(tonumber(file.width) * options.scale) local height = math.floor(tonumber(file.height) * options.scale) if (sizeWidth and sizeWidth < width) or (sizeHeight and sizeHeight < height) then width = sizeWidth height = sizeHeight end size = "" if width then size = width end if height then size = size .. "x" .. height end size = size .. "px" options = utilsTable.merge({}, options, { size = size, }) end local classes = utilsTable.compact({options.notPageImage and "notpageimage", options.isPixelArt and "pixel-art"}) if #classes > 0 then options.class = table.concat(classes, " ") end return utilsMarkup.file(originalFilename, options), checkExists and true or nil end function p.dimensions(size) if not size then return nil end local s, e s, e = size:find("^[0-9]+") local width = s and size:sub(s, e) or "" s, e = size:find("x[0-9]+") local height = s and size:sub(s+1, e) or "" return tonumber(width), tonumber(height) end function h.noimage(filename, sizeWidth, sizeHeight, options) local uploadUrl = mw.uri.fullUrl("Special:Upload") uploadUrl:extend({ wpDestFile = filename }) local options = utilsTable.merge({}, options, { link = tostring(uploadUrl), class = "notpageimage", }) -- Make sure thumbnail for 'no image' is no less than 100x100px if (sizeWidth and sizeWidth < 100) or (sizeHeight and sizeHeight < 100) then options.size = "100px" end return utilsMarkup.file("File:No Image Upload.png", options) end function p.gameImage(game, subject, type, options) options = options or {} local parts = utilsTable._filter(utilsString.notEmpty)({game, subject, type}) local filename = table.concat(parts, " ") .. ".png" if Franchise.graphics(game) == "2D" then options.isPixelArt = true end return p.image(filename, options) end function p.icon(game, subject, options) local type = "Icon" if Franchise.graphics(game) == "2D" then type = "Sprite" end options = options or {} options.isPixelArt = true return p.gameImage(game, subject, type, options) end function p.logo(code, options) local filename = Franchise.logo(code) return p.image(filename, options) end function p.Schemas() local optionsSchema = { type = "record", properties = { { name = "size", type = "string", desc = "Image size in pixels.", }, { name = "scale", type = "number", desc = "Image scaling factor — the original image size is multitplied by <code>scale</code>. If both <code>scale</code> and <code>size</code> are present, the value which results in the smaller image will be used. <b>By default this uses an {{Mediawiki|Manual:$wgExpensiveParserFunctionLimit|expensive parser function}}</b>.", }, { name = "scaleUsingCargo", type = "boolean", desc = "If set to true, then a Cargo query is used to determine the original image size for the <code>scale</code> option above. You can use this to avoid hitting expensive parser function limit. This option has an additional performance cost of roughly 2-5 milliseconds per image.", }, { name = "link", type = "string", desc = "Name of a page on the wiki or an external URL for the image thumbnail to link to.", }, { name = "caption", type = "string", desc = "[https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img Alt text] for the image.", }, { name = "notPageImage", type = "boolean", desc = "If true, the image will never appear as the page's representative image in [https://www.mediawiki.org/wiki/Page_Previews page previews].", }, { name = "checkExists", type = "boolean", default = "true", desc = "<p>If set to <code>false</code> then the function skips the file existence check. A red link is returned instead of the 'please upload' placeholder.</p><p>This may be needed to prevent [[:Category:Pages with too many expensive parser function calls|too many expensive parser function calls]] from occurring on a page.</p>", }, } } local franchiseCode = { required = true, type = "string", desc = "A [[Data:Franchise|franchise code]]." } return { image = { filename = { required = true, type = "string", desc = "Filename of the image, with or without the namespace prefix.", }, options = { type = "record", properties = utilsTable.concat(optionsSchema.properties, { { name = "isPixelArt", type = "boolean", desc = "If <code>true</code> the image will be rendered using nearest-neighbor interpolation, which prevents pixel art (sprites, 2D game screenshots) from appearing blurry in thumbnails.", }, }) }, }, gameImage = { game = franchiseCode, subject = { type = "string", required = true, }, type = { type = "string", required = true, enum = {"", "Artwork", "Icon", "Model", "Render", "Screenshot", "Sprite", "Texture"}, }, options = optionsSchema, }, icon = { game = franchiseCode, subject = { type = "string", required = true, }, options = optionsSchema, }, logo = { code = franchiseCode, optons = optionsSchema, }, } end function p.Documentation() return { image = { desc = "A higher-level version of [[Module:UtilsMarkup#file|utilsMarkup.file]] with awareness of whether the file exists or not.", params = {"filename", "options"}, returns = { "Wikitext rendering an image thumbnail.", "A boolean — true if the image exists, false otherwise.", }, cases = { { args = {"File:TWW Great Fairy Figurine Model.png", { link = "Great Fairy", size = "100px" }}, expect = {"[[File:TWW Great Fairy Figurine Model.png|100px|link=Great Fairy]]", true} }, { desc = "If file does not exist, show 'click to upload' thumbnail which links to [[Special:Upload]].", args = {"File:TWWHD Great Fairy Figurine Model.png", { link = "Great Fairy", size = "150px", }}, expect = {"[[File:No Image Upload.png|150px|link=//zeldawiki.wiki/wiki/Special:Upload?wpDestFile=TWWHD+Great+Fairy+Figurine+Model.png|class=notpageimage]]", false} }, { desc = "'No image' thumbnail has minimum 100px width, because it is illegible at smaller sizes.", args = {"File:TWWHD Great Fairy Figurine Model.png", { size = "64px", }}, expect = {"[[File:No Image Upload.png|100px|link=//zeldawiki.wiki/wiki/Special:Upload?wpDestFile=TWWHD+Great+Fairy+Figurine+Model.png|class=notpageimage]]", false}, }, { desc = "<code>checkExists = false</code> skips the existence check and simply render a red link", args = {"File:TWWHD Great Fairy Figurine Model.png", { link = "Great Fairy", size = "100px", checkExists = false, }}, expect = {"[[File:TWWHD Great Fairy Figurine Model.png|100px|link=Great Fairy]]", nil} }, { desc = "Scaling factor.", args = {"File:TMC Vaati Sprite.png", { scale = 2 }}, expect = {"[[File:TMC Vaati Sprite.png|48x56px]]", true} }, { desc = "The <code>isPixelArt</code> options ensures that sprites do not appear blurry.", args = {"File:TMC Vaati Sprite.png", { scale = 2, isPixelArt = true }}, expect = {"[[File:TMC Vaati Sprite.png|48x56px|class=pixel-art]]", true}, }, { desc = "Scaling using Cargo instead of an expensive parser function.", args = {"File:TMC Vaati Sprite.png", { scale = 2, scaleUsingCargo = true, isPixelArt = true }}, expect = {"[[File:TMC Vaati Sprite.png|48x56px|class=pixel-art]]", true}, }, { desc = "If both <code>scale</code> and <code>size</code> are specified, the one resulting in the smaller image is used.", args = {"File:TMC Vaati Sprite.png", { scale = 2, size = "80px", isPixelArt = true }}, expect = {"[[File:TMC Vaati Sprite.png|48x56px|class=pixel-art]]", true}, }, { args = {"File:TMC Vaati Sprite.png", { scale = 10, size = "80px", isPixelArt = true }}, expect = {"[[File:TMC Vaati Sprite.png|80px|class=pixel-art]]", true}, }, { desc = "Applying <code>notpageimage</code>.", args = {"File:TMC Vaati Sprite.png", { notPageImage=true, isPixelArt = true }}, expect = {"[[File:TMC Vaati Sprite.png|class=notpageimage pixel-art]]", true}, }, }, }, gameImage = { desc = "A specialized version of [[Module:File#image|image]] that infers the filename from game, subject, and type.", params = {"game", "subject", "type", "options"}, returns = { "A <code>string</code> of wikitext that renders a thumbnail.", "A boolean — true if the image exists, false otherwise.", }, cases = { { args = {"TWW", "Great Fairy Figurine", "Model", { link = "Great Fairy", size = "100px" }}, expect = {"[[File:TWW Great Fairy Figurine Model.png|100px|link=Great Fairy]]", true} }, } }, icon = { params = {"game", "subject", "options"}, returns = "An icon thumbnail for the subject in the given game.", cases = { { args = {"LANS", "Pineapple"}, expect = "[[File:LANS Pineapple Icon.png|class=pixel-art]]" }, { args = {"LADX", "Pineapple"}, expect = "[[File:LADX Pineapple Sprite.png|class=pixel-art]]" }, } }, logo = { params = {"code", "options"}, returns = { "Given a valid [[Data:Franchise|franchise code]], returns a logo thumbnail.", "A boolean indicating whether a logo exists for the game yet.", }, cases = { { args = {"TWW", { size = "200px" }}, expect = {"[[File:TWW English Logo.png|200px]]", true} }, { args = {"SSB4", { size = "200px" }}, expect = {"[[File:SSB4 Logo.png|200px]]", true}, }, { args = {"SS (Himekawa)", { size = "200px" }}, expect = {"[[File:Viz Media Logo.svg|200px]]", true} }, { args = {"TLoZ (Mishouzaki)", { size = "200px" }}, expect = {"[[File:TLoZ (Mishouzaki) Manga Cover Artwork.png|200px]]", true}, }, { args = {"TAoL (Mishouzaki)", { size = "200px" }}, expect = {"[[File:No Image Upload.png|200px|link=//zeldawiki.wiki/wiki/Special:Upload?wpDestFile=TAoL+%28Mishouzaki%29+Manga+Cover+Artwork.png|class=notpageimage]]", false}, }, { args = {"E", { size = "200px" }}, expect = {"[[File:The Legend of Zelda Encyclopedia Cover.png|200px]]", true}, }, { args = {"TMoL", { size = "200px" }}, expect = {"[[File:Misadventures Link logo2.png|200px]]", true}, }, } } } end return p