Module:Icon

--- A library that other modules can use to create icon images with automatic prefix/suffix settings for items, characters, and weapons. -- (Other icon types must have their types/suffixes specified manually.) -- -- Note: this module is (currently) NOT related to Template:Icon. -- -- @script Icon

local TemplateData = require('Module:TemplateData')

local p = {}

local FOOD_PREFIXES = { ["Suspicious "] = 1, ["Delicious "] = 1, }

-- icon arg defaults for template data -- uses list format to ensure that the order is the same when added to another list of args using `p.addIconArgs` local ICON_ARGS = { {name="name", label="Name", description="The name of the {labelPrefix}image.", example={"Furnishing Blueprint", "Fruit Paste Bait", "Anemo"} },	{name="link", displayType="wiki-page-name", defaultFrom="name", label="Link", description="The page that the {labelPrefix}image links to." },	{name="extension", alias="ext", default="png", label="File Extension", description="The file extension for the {labelPrefix}image.", example={"png", "jpg", "jpeg", "gif"}, },	{name="type", displayDefault='determined based on {labelPrefix}Name; "Item" if unknown', label="File Prefix", description="The file prefix to prepend to {labelPrefix}Name.", example={"Item", "Weapon", "Character", "Icon"} },	{name="suffix", displayDefault='determined based on {labelPrefix}Name/{labelPrefix}Type', label="File Suffix", description="The file suffix to append to {labelPrefix}Name.", example={"Icon", "Thumb", "2nd", "White"} },	{name="size", default="20", label="Size", description="The height and width of the {labelPrefix}image in pixels. A single number will be used for both height and width; to specify them separately, use \"wxh\" format.", example={"20", "20x10", "20x", "x20"} },	{name="alt", defaultFrom="name", displayDefault="determined based on {labelPrefix}Name, and updated based on {labelPrefix}Character Outfit and {labelPrefix}Weapon Ascension Phase", label="Alt Text", description="The alt text of the {labelPrefix}image.", },	{name="outfit", alias="o", label="Character Outfit", description="Name of the Outfit equipped on the character in the {labelPrefix}image. (Ignored if not a character.)", example="Sea Breeze Dandelion" },	{name="ascension", alias="a", type="number", default=0, label="Weapon Ascension Phase", description="Ascension Phase of the weapon in the {labelPrefix}image. (Weapon image changes for ascension 2 or higher. Ignored if not a weapon.)", example={"1", "2"} }, }

--- Returns a table version of the input. -- @param v A table or an item that belongs in a table. -- @return {table} local function ensureTable(v) if type(v) == "table" then return v end return {v} end

--- A special constant that can be used as a value in the `overrides` parameter for `addIconArgs` -- to disable the given base config or TemplateData key. p.EXCLUDE = {};

--- Copy entries from b into a, excluding values that are `p.EXCLUDE`. -- @param {table} a Table to insert into. -- @param {table} b Table to copy from. local function copyTable(a, b)	for k, v in pairs(b) do 		if v == p.EXCLUDE then v = nil end a[k] = v	end end

--- A function that other modules can use to append Icon arguments to their template data. -- @param {TemplateData#ArgumentConfigList} base The template data to append to. -- @param {string} namePrefix A string to prepend to all parameter names. -- @param {string} labelPrefix A string to prepend to all parameter labels --   (the names displayed in documentation). -- @param {TemplateData#ArgumentConfigMap} overrides A table of overrides to configure the template data. --   Keys should be parameter names, and values are tables of TemplateData --   configuration objects to merge with the default configuration. --   These configuration objects can use @{Icon.EXCLUDE} as values to disable --   the corresponding default configuration key (e.g., to disable a default value --    or alias without adding a new one). function p.addIconArgs(baseConfigs, namePrefix, labelPrefix, overrides) for _, config in ipairs(ICON_ARGS) do		local override = overrides[config.name] if override ~= false and override ~= p.EXCLUDE then local c = {} copyTable(c, config) c.name = namePrefix .. c.name c.label = labelPrefix .. c.label if c.defaultFrom then c.defaultFrom = namePrefix .. c.defaultFrom end if c.alias then local aliases = {} for _, a in ipairs(ensureTable(c.alias)) do table.insert(aliases, namePrefix .. a)				end c.alias = aliases end if override then copyTable(c, override) end if c.description then c.description = c.description:gsub("{labelPrefix}", labelPrefix) end if c.displayDefault then c.displayDefault = c.displayDefault:gsub("{labelPrefix}", labelPrefix) end table.insert(baseConfigs, c)		end end end

local function extendTable(base, extra) out = {} setmetatable(out, {		__index = function(t, key)			local val = base[key]			if val ~= nil then return val else return extra[key] end		end	}) return out end local characters = mw.loadData('Module:Card/characters') local ALL_DATA = {-- list of {type, data} in the order that untyped items should get checked {"Element", {Anemo={}, Cryo={}, Dendro={}, Electro={}, Geo={}, Hydro={}, Pyro={}}}, {"Character", characters}, {"Weapon", mw.loadData('Module:Card/weapons')}, {"Food", mw.loadData('Module:Card/foods')}, {"Furnishing", mw.loadData('Module:Card/furnishings')}, {"Book", mw.loadData('Module:Card/books')}, {"Outfit", mw.loadData('Module:Card/outfits')}, {"Artifact Set", mw.loadData('Module:Card/artifact sets')}, {"Item", extendTable(characters, mw.loadData('Module:Card/items'))} -- allow characters to be items }

--- A basic image type with filename prefix/suffix support. -- Extends @{TemplateData#TableWithDefaults} with image-related properties, -- allowing default property values to be customized after creation. -- -- Image objects are created by @{Icon.createIcon}. -- Use @{Image:buildString} to convert to wikitext. -- @type Image local IMAGE_ARGS = { size = {default=""}, name = {default="", alias=1}, prefix = {default=""}, prefixSeparator = {default=" "}, suffix = {default=""}, suffixSeparator = {default=" "}, extension = {default="png", alias="ext"}, link = {defaultFrom="name"}, alt = {defaultFrom="name"}, }

local function buildImageFullPrefix(img) local prefix = img.prefix if prefix ~= '' then return prefix .. img.prefixSeparator end return '' end local function buildImageFullSuffix(img) local suffix = img.suffix if suffix ~= '' then return img.suffixSeparator .. suffix end return '' end local function buildImageFilename(img) if img.name ==  then return  end return 'File:' .. buildImageFullPrefix(img) .. img.name .. buildImageFullSuffix(img) .. '.' .. img.extension end local function buildImageSizeString(img) local size = img.size if size == nil or size == '' then return '' end size = tostring(size) if size:find('x', 1, true) then return size .. 'px' end return size .. 'x' .. size .. 'px' end -- Returns wikitext that will display this image. @return {string} Wikitext @function Image:buildString -- local function buildImageString(img) local filename = buildImageFilename(img) if filename ==  then return  end return  .. buildImageSizeString(img) .. '|link=' .. img.link .. '|alt=' .. img.alt ..  end

-- Creates an `Image` object with the given properties. @param {table} args A table of `Image` properties that to assign to the new `Image`. @see @{Image} for property documentation @return {Image} The new `Image` object. @constructor -- local function createImage(args) local out if type(args) == 'table' then out = TemplateData.processArgs(args, IMAGE_ARGS, {preserveDefaults=true}) else out = {name = args} end -- add buildString function to image directly, not to image.nonDefaults rawset(out, 'buildString', buildImageString) return out end

--- The name of the image. Used to determine filename. -- @property {string} Image.name

--- Maximum image size using wiki image syntax. -- Unlike regular wiki image syntax, a single number specifies both height and width. -- @property {string} Image.size

--- Image file prefix. -- @property {string} Image.prefix

--- Appended to `prefix` if `prefix` is not empty. Defaults to a single space. -- @property[opt] {string} Image.prefixSeparator

--- Image file suffix. -- @property {string} Image.suffix

--- Prepended to `suffix` if `suffix` is not empty. Defaults to a single space. -- @property {string} Image.suffixSeparator

--- Image file extension. (Alias: `ext`.) -- @property {string} Image.extension

--- Image link. Also sets title (hover tooltip). Defaults to `name`. -- @property {string} Image.link

--- Image alt text. Defaults to `name`. -- @property {string} Image.alt

--- A helper function for other modules to get icon args with a prefix from the given table. -- @param {table} args A table containing icon args (and probably other args). -- @param {string} prefix Prefix for keys to use when looking up entries from `args`. --   If not specified, no prefix is used. -- @return {table} A table containing only the extracted args, with the prefix removed from its keys. function p.extractIconArgs(args, prefix) prefix = prefix or '' local out = TemplateData.createObjectWithDefaults for _, config in pairs(ICON_ARGS) do out.defaults[config.name] = args.defaults[prefix .. config.name] out.nonDefaults[config.name] = args.nonDefaults[prefix .. config.name] end return out end

--- A helper function that removes one of the given prefixes from the given string. -- @param {string} str The string from which to strip a prefix -- @param {table} prefixes A table whose keys are the possible prefixes to strip from `str` -- @return {string} The string with the prefix removed. -- @return[opt] {string} The prefix that was removed, or `nil` if no prefix was found. function p.stripPrefixes(str, prefixes) for prefix, _ in pairs(prefixes) do		if string.sub(str, 1, string.len(prefix)) == prefix then return string.sub(str, string.len(prefix) + 1), prefix end end return str end

--[[   The main entry point for other modules.    Creates and returns an Image object with the given arguments.    Infers `args.type` if `args.name` matches a known item/character/weapon.    @param {table} args Table containing the arguments:    @param[opt] {string} args.name The name of the image.                         Used to determine file name and set the default link and alt text.                         If not specified, the output `Image` will produce empty wikitext.    @param[opt] {string} args.link Image link. Also sets title (hover tooltip).                         Defaults to `name`.    @param[opt] {string} args.extension (alias: `ext`) Image file extension.                         Defaults to "png".    @param[opt] {string} args.type Image type. Used to determine file prefix and default file suffix.                         If not specified, type will be inferred if `args.name`                         is a known item; otherwise, defaults to "Item".    @param[opt] {string} args.suffix File suffix override.    @param[opt] {string} args.size Maximum image size using wiki image syntax.                         Unlike regular wiki image syntax, specifying a single number will                         set both height and width.    @param[opt] {string} args.alt Image alt text. Defaults to `args.name`, but                         also changes with `args.outfit` or `args.ascension`.    @param[opt] {string} args.outfit Character outfit. Only applies to character images.    @param[opt] {number} args.ascension Weapon ascension level. Only applies to weapon images.    @param[opt] {string} prefix Prefix to use when looking up arguments in `args`.    @return {Image} The image for given arguments.      Use `Image:buildString` to get the wikitext for the image.    @return {string} The image type.    @return {TableWithDefaults} Associated data for given item (e.g., rarity, display name). --]] function p.createIcon(args, prefix) if prefix then args = p.extractIconArgs(args, prefix) end args = TemplateData.processArgs(args, ICON_ARGS, {preserveDefaults=true}) local baseName, prefix = p.stripPrefixes(args.name or '', FOOD_PREFIXES) -- determine type by checking for data local image_type, data for i, v in ipairs(ALL_DATA) do		image_type = v[1] if args.type == nil or args.type == image_type then data = v[2][baseName] if data then break end end end image_type = args.type or image_type -- in case args.type isn't in ALL_DATA (e.g., Enemy, Wildlife) local image = createImage(args) image.prefix = image_type -- set defaults and load data based on type if image_type == "Character" then if args.outfit ~= nil then image.prefix = "Outfit" image.name = args.outfit image.defaults.alt = image.alt .. ' (' .. args.outfit .. ' outfit)' end image.defaults.suffix = "Thumb" elseif image_type == "Weapon" then if (args.ascension > 1) then image.defaults.suffix = "2nd" image.defaults.alt = image.alt .. ' (ascension phase ' .. args.ascension .. ')' end elseif image_type == "Food" or image_type == "Furnishing" or image_type == "Book" or image_type == "Outfit" or image_type == "Artifact Set" then image.prefix = "Item" elseif image_type == "Enemy" or image_type == "Wildlife" then image.defaults.suffix = "Icon" end return image, image_type, data end

return p