Terraria Wiki

  • Discussions are now available on the Terraria Wiki.
  • Miss the old Hydra Skin? Try out our Hydralize gadget! Visit the preferences page while logged in and turn on the gadget.

READ MORE

Terraria Wiki
Register
(use luacache.)
(crap)
(12 intermediate revisions by 3 users not shown)
Line 1: Line 1:
  +
---Holds the tables with the l10n information for the different languages, taken from the l10n submodule.
 
------- l10n info --------------
 
 
local l10n_info = mw.loadData('Module:Item/l10n')
 
local l10n_info = mw.loadData('Module:Item/l10n')
   
  +
---Holds the l10n information for the current language, as key-value pairs.
------- The following is not related to l10n. --------------
 
 
local l10n_table
  +
  +
---The current language. Determines which l10n table to use.
 
local lang
   
 
local trim = mw.text.trim
 
local trim = mw.text.trim
Line 10: Line 13:
 
local cache = require 'mw.ext.LuaCache'
 
local cache = require 'mw.ext.LuaCache'
   
  +
local should_cache = true
  +
  +
---A cached version of the current frame, the interface to the parser.
 
local currentFrame
 
local currentFrame
  +
---Holds the arguments from the template call.
 
local args_table
 
local args_table
local lang
 
local l10n_table
 
   
  +
local l10n = function(key)
 
  +
---Return the l10n string associated with the `key`.
  +
---@param key string
  +
---@return string
 
local function l10n(key)
 
return l10n_table[key] or l10n_info['en'][key]
 
return l10n_table[key] or l10n_info['en'][key]
 
end
 
end
   
  +
---Return a trimmed version of the value of the template parameter with the specified `key`.
local getArg = function(key)
 
  +
---Return `nil` if the parameter is empty or unset.
  +
---@param key string|number
  +
---@return string|nil
 
local function getArg(key)
 
local value = args_table[key]
 
local value = args_table[key]
 
if not value then
 
if not value then
Line 31: Line 44:
 
end
 
end
   
  +
---Convert a string of parameters in a `@param1:value^@param2:value^` format to a table.
local function parse(str)
 
  +
---Change the `name` and `text` parameters to `1` and `2`, respectively.
  +
---@param paramstr string
  +
---@return table
 
local function parse(paramstr)
 
local args = {}
 
local args = {}
for s in string.gmatch(str, '%b@^') do
+
for s in string.gmatch(paramstr, '%b@^') do
 
local k,v = string.match(s, '^@(.-):(.*)^$')
 
local k,v = string.match(s, '^@(.-):(.*)^$')
 
args[k] = v
 
args[k] = v
Line 42: Line 59:
 
end
 
end
   
  +
---Split the `str` on each `div` in it and return the result as a table.
-- credit: http://richard.warburton.it
+
---Original version credit: http://richard.warburton.it. This version trims each substring.
-- this version is with trim.
 
  +
---@param div string
local function explode(div,str)
 
  +
---@param str string
  +
---@return table|boolean
 
local function explode(div,str)
 
if (div=='') then return false end
 
if (div=='') then return false end
 
local pos,arr = 0,{}
 
local pos,arr = 0,{}
Line 56: Line 76:
 
end
 
end
   
  +
---Extract scale, width, and height from an input string. Up to two of the three can be empty in the input.
  +
---Example: `5x7px*0.75` → `0.75`, `5`, `7`
  +
---@param size string
  +
---@return string basescale
  +
---@return number width
  +
---@return number height
 
local function parseSize(size)
 
local function parseSize(size)
 
if not size then return end
 
if not size then return end
Line 69: Line 95:
 
end
 
end
   
  +
---Return width, height, and caching date for the specified `imagename` from the Imageinfo cargo table.
local function getInfoFromCargo(image)
 
  +
---@param imagename string
  +
---@return number width
  +
---@return number height
  +
---@return string cached
 
local function getInfoFromCargo(imagename)
 
-- try to get from cargo cache
 
-- try to get from cargo cache
 
local result = mw.ext.cargo.query('Imageinfo', 'width, height, cached', {
 
local result = mw.ext.cargo.query('Imageinfo', 'width, height, cached', {
  +
-- escape apostrophes in the imagename input
where = 'image='.. "'"..image:gsub("'", "\\'"):gsub("'", "\\'").."'",
+
where = 'image='.. "'"..imagename:gsub("'", "\\'"):gsub("'", "\\'").."'",
 
orderBy = "cached DESC",
 
orderBy = "cached DESC",
 
limit = 1,
 
limit = 1,
Line 81: Line 113:
 
end
 
end
   
  +
---Store width and height of the specified `imagename` to the Imageinfo cargo table and return them.
local function storeInfoToCargo(image)
 
  +
---Width and height are computed via the `#imgw:` and `#imgh:` parser functions, respectively.
local width, height
 
  +
---@param imagename string
width = tonumber(currentFrame:callParserFunction( '#imgw', image))
 
  +
---@return number width
if width and width ~= 0 then -- save one expensive call when the file is not a valid image.
 
  +
---@return number height
height = tonumber(currentFrame:callParserFunction( '#imgh', image))
 
 
local function storeInfoToCargo(imagename)
if height and height ~= 0 then
 
  +
-- don't cache {{item}}'s result when parsing imagesize fails.
currentFrame:callParserFunction('#cargo_store:_table=Imageinfo',{
 
  +
should_cache = false
image = image,
 
  +
local imageTitle = mw.title.new("File:" .. imagename)
width = width,
 
  +
local width, height = imageTitle.file.width, imageTitle.file.height
height = height,
 
 
if width and width ~= 0 and height and height ~= 0 then
cached = os.time(),
 
  +
should_cache = true -- ok, cache it.
})
 
 
currentFrame:callParserFunction('#cargo_store:_table=Imageinfo',{
end
 
 
image = imagename,
 
width = width,
 
height = height,
 
cached = os.time(),
 
})
 
end
 
end
 
return width, height
 
return width, height
 
end
 
end
   
  +
---Retrieve the dimensions of the specified `image` from the Imageinfo cargo table.
local function getSizeInfo(image)
 
  +
---If it doesn't have any data for the image yet, store it.
local width, height, cached = getInfoFromCargo(image)
 
  +
---@param imagename string
  +
---@return number width
  +
---@return number height
  +
local function getSizeInfo(imagename)
 
local width, height, cached = getInfoFromCargo(imagename)
 
-- cache missed, init cache
 
-- cache missed, init cache
 
if not cached then
 
if not cached then
width, height = storeInfoToCargo(image)
+
width, height = storeInfoToCargo(imagename)
 
end
 
end
 
if width == 0 then width = nil end
 
if width == 0 then width = nil end
Line 109: Line 151:
 
end
 
end
   
local function getImageSize(image, width, height, scale, maxwidth, maxheight)
+
---Compute the final width and height of the image.
  +
---If necessary, retrieve data from or store data to the Imageinfo cargo table.
  +
---@param imagename string
  +
---@param width number
  +
---@param height number
  +
---@param scale number
  +
---@param maxwidth number
  +
---@param maxheight number
  +
---@return number width
  +
---@return number height
  +
local function getImageSize(imagename, width, height, scale, maxwidth, maxheight)
 
-- get size info from image file itself (may be expensive)
 
-- get size info from image file itself (may be expensive)
local w, h = getSizeInfo(image) -- store data to cache
+
local w, h = getSizeInfo(imagename) -- store data to cache
   
  +
-- if width and height are not given as input, but scale/maxwidth/maxheight are, then
  +
-- set width and height to the original dimensions of the image
 
if not width and not height and (scale or maxwidth or maxheight) then
 
if not width and not height and (scale or maxwidth or maxheight) then
 
width, height = w, h
 
width, height = w, h
Line 123: Line 177:
 
end
 
end
   
-- apply maxwidth/maxheight.
+
-- apply maxwidth/maxheight
 
if maxwidth then
 
if maxwidth then
 
if width then
 
if width then
Line 139: Line 193:
 
end
 
end
   
  +
-- round to natural numbers
-- rounding
 
 
if width then width = math.ceil(width) end
 
if width then width = math.ceil(width) end
 
if height then height = math.ceil(height) end
 
if height then height = math.ceil(height) end
Line 146: Line 200:
 
end
 
end
   
  +
---Extract width and height from an input string.
  +
---Example: `6x9px` → `6`, `9`
  +
---@param maxsize string
  +
---@return number maxwidth
  +
---@return number maxheight
 
local function parseMaxSize(maxsize)
 
local function parseMaxSize(maxsize)
 
if not maxsize then return end
 
if not maxsize then return end
Line 155: Line 214:
 
end
 
end
   
  +
---Assemble the final wikicode for an image.
local function imagecode(image, link, text, size, scale, maxsize)
 
  +
---@param imagename string
local image_output = '[[File:' .. image .. '|link='.. link .. '|' .. text
 
  +
---@param link string
  +
---@param text string
  +
---@param size string As accepted by the `[[File:` syntax, e.g. `5x7px*0.75`.
  +
---@param scale number This will be multiplied by the scale in `size`, if necessary.
  +
---@param maxsize string
  +
---@return string
 
local function imagecode(imagename, link, text, size, scale, maxsize)
 
local image_output = '[[File:' .. imagename .. '|link='.. link .. '|' .. text
 
if size or scale or maxsize then
 
if size or scale or maxsize then
local basescale, width, height = parseSize(size) -- width,height: number or nil
+
local basescale, width, height = parseSize(size) -- width, height: number or nil (basescale is string!)
scale = (tonumber(scale) or 1) * (tonumber(basescale) or 1)
+
scale = (tonumber(scale) or 1) * (tonumber(basescale) or 1) -- combine the scale parameter and scale from the size parameter
 
if scale == 0 or scale == 1 then
 
if scale == 0 or scale == 1 then
 
scale = nil
 
scale = nil
 
end
 
end
 
local maxwidth, maxheight = parseMaxSize(maxsize)
 
local maxwidth, maxheight = parseMaxSize(maxsize)
width, height = getImageSize(image, width, height, scale, maxwidth, maxheight) -- can be 0
+
width, height = getImageSize(imagename, width, height, scale, maxwidth, maxheight) -- can be 0
 
if width or height then
 
if width or height then
return image_output .. '|' .. (width or '') .. 'x' .. (height or '') .. 'px]]'
+
image_output = image_output .. '|' .. (width or '') .. 'x' .. (height or '') .. 'px'
else
 
return image_output .. ']]'
 
 
end
 
end
else
 
return image_output .. ']]'
 
 
end
 
end
 
return image_output .. ']]'
 
end
 
end
   
  +
---Return the full `[[File:` wikicode for each image in the input (multiple are separated with `/`).
  +
---@param image string
  +
---@param link string
  +
---@param text string
  +
---@param size string
  +
---@param scale string
  +
---@param maxsize string
  +
---@return string
 
local function images(image, link, text, size, scale, maxsize)
   
local function images(image, link, text, size, scale, maxsize)
 
 
 
if not image:find('/') then
 
if not image:find('/') then
  +
-- there is only one image in the input
 
return imagecode(image, link, text, size, scale, maxsize)
 
return imagecode(image, link, text, size, scale, maxsize)
 
end
 
end
   
  +
-- there are multiple images in the input, separated with a slash
 
image = explode('/', image)
 
image = explode('/', image)
 
local result = ''
 
local result = ''
 
if size and size:find('/') then
 
if size and size:find('/') then
  +
-- there are multiple sizes in the size parameter
size = explode('/', size)
+
size = explode('/', size) -- so turn it into a table
for i, v in ipairs(image) do
+
for i, v in ipairs(image) do -- iterate over the images
result = result .. imagecode(v, link, text, size[i], scale, maxsize)
+
result = result .. imagecode(v, link, text, size[i], scale, maxsize) -- create the wikicode (using the respective size)
 
end
 
end
 
else
 
else
for i, v in ipairs(image) do
+
for i, v in ipairs(image) do -- iterate over the images
result = result .. imagecode(v, link, text, size, scale, maxsize)
+
result = result .. imagecode(v, link, text, size, scale, maxsize) -- create the wikicode
 
end
 
end
 
end
 
end
Line 197: Line 271:
 
end
 
end
   
  +
---Return a string like `Internal Item ID: `, depending on the `_type`.
local getIdText = function(_type)
 
  +
---@param _type '"item"'|'"tile"'|'"wall"'|'"npc"'|'"mount"'|'"buff"'|'"projectile"'|'"armor"'
  +
---@return string
 
local function getIdText(_type)
 
local id_text
 
local id_text
 
if _type == 'item' then -- a shortcut for faster
 
if _type == 'item' then -- a shortcut for faster
Line 225: Line 302:
 
return {
 
return {
   
parse = parse,
+
parse = parse,
 
go = function(frame, args)
 
go = function(frame, args)
 
-- cache?
 
-- cache?
 
if not args then
 
if not args then
local cached = cache.get(':item:'..frame.args[1])
+
local cached = cache.get(':_item:' .. frame.args[1])
 
if cached then
 
if cached then
 
return cached
 
return cached
Line 243: Line 320:
 
local _arg1 = getArg(1) or ''
 
local _arg1 = getArg(1) or ''
 
local _nolink = getArg('nolink')
 
local _nolink = getArg('nolink')
local _link = _nolink and '' or getArg('link') or frame:expandTemplate{ title = 'tr', args = {_arg1, link='y', lang=lang} } -- now: _link == '' means nolink.
+
local _link = _nolink and '' or getArg('link') or frame:expandTemplate{ title = 'tr', args = {_arg1, link='y', lang=lang} } -- now: _link == '' means nolink
   
 
local text = getArg(2) or ''
 
local text = getArg(2) or ''
   
  +
-- hovertext: {{tr|_arg1}} or text or _link (in that order)
 
local hovertext
 
local hovertext
 
if _arg1 ~= '' then
 
if _arg1 ~= '' then
Line 256: Line 334:
 
end
 
end
   
  +
-- set output flags
local class = 'i'
 
 
 
local output_image, output_text, output_table = true, true, false
 
local output_image, output_text, output_table = true, true, false
 
local _mode = getArg('mode')
 
local _mode = getArg('mode')
Line 269: Line 346:
 
end
 
end
 
end
 
end
  +
 
local class = 'i'
   
 
local image_output, text_output
 
local image_output, text_output
  +
-- get wikicode for the image(s)
 
if output_image then
 
if output_image then
 
local image_arg = getArg('image')
 
local image_arg = getArg('image')
Line 291: Line 371:
 
image_output = ''
 
image_output = ''
 
end
 
end
  +
-- get wikicode for the text
 
if output_text then
 
if output_text then
local _note, _note2, _showid, _id = getArg('note'), getArg('note2'), getArg('showid'), getArg('id')
+
local _note, _note2, _showid, _id, _icon = getArg('note'), getArg('note2'), getArg('showid'), getArg('id'), getArg('icons') -- get info from arguments
   
  +
-- prepare: display ID?
 
if _id and not _showid then
 
if _id and not _showid then
 
_showid = true
 
_showid = true
Line 301: Line 383:
 
end
 
end
   
  +
-- prepare: wrap?
 
local _wrap
 
local _wrap
 
if _showid or _note2 then
 
if _showid or _note2 then
Line 308: Line 391:
 
end
 
end
   
  +
-- prepare: link and display text
 
if _link ~= '' then
 
if _link ~= '' then
 
if text == _link then
 
if text == _link then
Line 318: Line 402:
 
end
 
end
   
  +
-- prepare: eicons
local _icon, icon = getArg('icons'), nil
+
local icon = nil
 
if _icon == 'n' or _icon == 'no' or _icon == 'off' then
 
if _icon == 'n' or _icon == 'no' or _icon == 'off' then
 
icon = ''
 
icon = ''
Line 325: Line 410:
 
end
 
end
   
  +
-- assemble HTML code
 
local content = text -- item name link text first.
 
local content = text -- item name link text first.
 
-- '-w' class means 'wrapmode', optimized for multiple lines of text. But it should be disabled for single line text.
 
-- '-w' class means 'wrapmode', optimized for multiple lines of text. But it should be disabled for single line text.
Line 333: Line 419:
 
content = content .. icon
 
content = content .. icon
 
end
 
end
-- note in next line
+
-- note in a new line
 
if _note then
 
if _note then
 
class = class .. ' -w'
 
class = class .. ' -w'
Line 339: Line 425:
 
end
 
end
 
else
 
else
-- note first
+
-- note in the same line
 
if _note then
 
if _note then
 
content = content .. '<span class="note">' .. _note .. '</span>'
 
content = content .. '<span class="note">' .. _note .. '</span>'
 
end
 
end
  +
-- eicons in the same line
 
if icon ~= '' then
 
if icon ~= '' then
 
content = content .. icon
 
content = content .. icon
 
end
 
end
  +
-- note2 in a new line
 
if _note2 then
 
if _note2 then
 
class = class .. ' -w'
 
class = class .. ' -w'
 
content = content .. '<div class="note">' .. _note2 .. '</div>'
 
content = content .. '<div class="note">' .. _note2 .. '</div>'
 
end
 
end
  +
-- id in a new line
 
if _showid then
 
if _showid then
 
class = class .. ' -w'
 
class = class .. ' -w'
local idtype = (getArg('type') or 'item'):lower()
+
local idtype = (getArg('type') or 'item'):lower()
 
if not _id then
 
if not _id then
  +
-- get ID automatically via {{itemIdFromName}} or the like
 
_id = frame:expandTemplate{ title = idtype .. 'IdFromName', args = {_arg1} }
 
_id = frame:expandTemplate{ title = idtype .. 'IdFromName', args = {_arg1} }
 
end
 
end
Line 365: Line 455:
 
end
 
end
   
  +
-- handle custom CSS
 
local _class, _css = getArg('class'), getArg('css')
 
local _class, _css = getArg('class'), getArg('css')
 
if _class then
 
if _class then
class = class .. ' ' .. _class
+
class = class .. ' ' .. _class -- add to existing classes
 
end
 
end
 
local attr = {class = class}
 
local attr = {class = class}
 
if _css then
 
if _css then
attr.style = _css
+
attr.style = _css -- set the style attribute to parameter value
end
 
 
if getArg('anchor') then
 
text_output = text_output .. '<div class="anchor" id="' .. frame:callParserFunction('anchorencode', _arg1) .. '"></div>'
 
 
end
 
end
   
 
local return_string
 
local return_string
 
if output_table then
 
if output_table then
  +
-- table output
 
attr.class = class
 
attr.class = class
local rowspan = getArg('rowspan')
+
local _rowspan = getArg('rowspan')
  +
local rowspan_text = (_rowspan and (' rowspan=' .. _rowspan) or '')
return_string = (rowspan and (' rowspan='..rowspan..' class="il1c" | ') or ' class="il1c" | ') .. mw.text.tag('span', attr, image_output) .. ' || class="il2c"'.. (rowspan and (' rowspan='..rowspan) or '')..' | ' .. mw.text.tag('span', attr, text_output)
 
  +
-- prepare the two cells
  +
local first_cell_pre = rowspan_text .. ' class="il1c"'
  +
local first_cell_content = mw.text.tag('span', attr, image_output)
  +
local second_cell_pre = rowspan_text .. ' class="il2c"'
  +
local second_cell_content = mw.text.tag('span', attr, text_output)
  +
-- combine
  +
return_string = first_cell_pre .. " | " .. first_cell_content .. " || " .. second_cell_pre .. " | " .. second_cell_content
 
else
 
else
  +
-- non-table output (text/image)
 
return_string = mw.text.tag('span', attr, image_output .. text_output)
 
return_string = mw.text.tag('span', attr, image_output .. text_output)
 
end
 
end
  +
--cache:
 
  +
-- cache output for later reuse
if not args then
+
if not args and should_cache then
cache.set(':item:'..frame.args[1], return_string, 3600*24) -- cache 24hr.
+
cache.set(':_item:' .. frame.args[1], return_string, 3600*24) -- cache for 24 hours
 
end
 
end
  +
  +
-- output
 
return return_string
 
return return_string
  +
 
end,
 
end,
  +
 
 
purge = function(frame)
  +
cache.delete(':_item:' .. frame.args[1]) -- delete that cache key.
 
end,
  +
  +
storeImageInfo = function(frame)
  +
currentFrame = frame
  +
local width, height = storeInfoToCargo(frame.args[1])
  +
if not width or width == 0 or not height or height == 0 then
  +
return
 
else
  +
return frame:callParserFunction{name = "#dplvar:set", args = {
  +
"_image_exist", "1",
  +
"_image_width", width,
  +
"_image_height", height,
  +
}}
 
end
  +
end,
  +
 
}
 
}

Revision as of 19:22, 28 August 2021

Lua logo Documentation The documentation below is transcluded from Module:Item/doc. (edit | history)

This module is intended to provide functionality to the {{item}} template.


---Holds the tables with the l10n information for the different languages, taken from the l10n submodule.
local l10n_info = mw.loadData('Module:Item/l10n')

---Holds the l10n information for the current language, as key-value pairs.
local l10n_table

---The current language. Determines which l10n table to use.
local lang

local trim = mw.text.trim
local cargo = mw.ext.cargo
local eicons = require('Module:Exclusive').simpleEicons
local cache = require 'mw.ext.LuaCache'

local should_cache = true

---A cached version of the current frame, the interface to the parser.
local currentFrame
---Holds the arguments from the template call.
local args_table


---Return the l10n string associated with the `key`.
---@param key string
---@return string
local function l10n(key)
	return l10n_table[key] or l10n_info['en'][key]
end

---Return a trimmed version of the value of the template parameter with the specified `key`.
---Return `nil` if the parameter is empty or unset.
---@param key string|number
---@return string|nil
local function getArg(key)
	local value = args_table[key]
	if not value then
		return nil
	end
	value = trim(value)
	if value == '' then
		return nil
	end
	return value
end

---Convert a string of parameters in a `@param1:value^@param2:value^` format to a table.
---Change the `name` and `text` parameters to `1` and `2`, respectively.
---@param paramstr string
---@return table
local function parse(paramstr)
	local args = {}
	for s in string.gmatch(paramstr, '%b@^') do
		local k,v = string.match(s, '^@(.-):(.*)^$')
		args[k] = v
	end
	args[1] = args['name']
	args[2] = args['text']
	return args
end

---Split the `str` on each `div` in it and return the result as a table.
---Original version credit: http://richard.warburton.it. This version trims each substring.
---@param div string
---@param str string
---@return table|boolean
local function explode(div,str)
	if (div=='') then return false end
	local pos,arr = 0,{}
	-- for each divider found
	for st,sp in function() return string.find(str,div,pos,true) end do
		arr[#arr + 1] = trim(string.sub(str,pos,st-1)) -- Attach chars left of current divider
		pos = sp + 1 -- Jump past current divider
	end
	arr[#arr + 1] = trim(string.sub(str,pos)) -- Attach chars right of last divider
	return arr
end

---Extract scale, width, and height from an input string. Up to two of the three can be empty in the input.
---Example: `5x7px*0.75` → `0.75`, `5`, `7`
---@param size string
---@return string basescale
---@return number width
---@return number height
local function parseSize(size)
	if not size then return end
	local basescale, width, height
	size, basescale = unpack(explode('*', size))
	if size ~= '' then
		width, height = unpack(explode('x', string.gsub(size, 'px', '')))
		width, height = tonumber(width), tonumber(height)
		if width == 0 then width = nil end
		if height == 0 then height = nil end
	end
	return basescale, width, height
end

---Return width, height, and caching date for the specified `imagename` from the Imageinfo cargo table.
---@param imagename string
---@return number width
---@return number height
---@return string cached
local function getInfoFromCargo(imagename)
	-- try to get from cargo cache
	local result = mw.ext.cargo.query('Imageinfo', 'width, height, cached', {
		-- escape apostrophes in the imagename input
		where = 'image='.. "'"..imagename:gsub("'", "\\'"):gsub("&#39;", "\\'").."'",
		orderBy = "cached DESC",
		limit = 1,
	})
	for _, row in ipairs(result) do
		return tonumber(row['width']), tonumber(row['height']), row['cached']
	end
end

---Store width and height of the specified `imagename` to the Imageinfo cargo table and return them.
---Width and height are computed via the `#imgw:` and `#imgh:` parser functions, respectively.
---@param imagename string
---@return number width
---@return number height
local function storeInfoToCargo(imagename)
	-- don't cache {{item}}'s result when parsing imagesize fails.
	should_cache = false
	local imageTitle = mw.title.new("File:" .. imagename)
	local width, height = imageTitle.file.width, imageTitle.file.height
	if width and width ~= 0 and height and height ~= 0 then
		should_cache = true -- ok, cache it.
		currentFrame:callParserFunction('#cargo_store:_table=Imageinfo',{
			image = imagename,
			width = width,
			height = height,
			cached = os.time(),
		})
	end
	return width, height
end

---Retrieve the dimensions of the specified `image` from the Imageinfo cargo table.
---If it doesn't have any data for the image yet, store it.
---@param imagename string
---@return number width
---@return number height
local function getSizeInfo(imagename)
	local width, height, cached = getInfoFromCargo(imagename)
	-- cache missed, init cache
	if not cached then
		width, height = storeInfoToCargo(imagename)
	end
	if width == 0 then width = nil end
	if height == 0 then height = nil end
	return width, height
end

---Compute the final width and height of the image.
---If necessary, retrieve data from or store data to the Imageinfo cargo table.
---@param imagename string
---@param width number
---@param height number
---@param scale number
---@param maxwidth number
---@param maxheight number
---@return number width
---@return number height
local function getImageSize(imagename, width, height, scale, maxwidth, maxheight)
	-- get size info from image file itself (may be expensive)
	local w, h = getSizeInfo(imagename) -- store data to cache

	-- if width and height are not given as input, but scale/maxwidth/maxheight are, then
	-- set width and height to the original dimensions of the image
	if not width and not height and (scale or maxwidth or maxheight) then
		width, height = w, h
	end

	-- apply scale to width/height if needed
	if scale then
		if width then width = width * scale end
		if height then height = height * scale end
	end

	-- apply maxwidth/maxheight
	if maxwidth then
		if width then
			if width > maxwidth then width = maxwidth end
		else
			if height then width = maxwidth end
		end
	end
	if maxheight then
		if height then
			if height > maxheight then height = maxheight end
		else
			if width then height = maxheight end
		end
	end

	-- round to natural numbers
	if width then width = math.ceil(width) end
	if height then height = math.ceil(height) end

	return width, height
end

---Extract width and height from an input string.
---Example: `6x9px` → `6`, `9`
---@param maxsize string
---@return number maxwidth
---@return number maxheight
local function parseMaxSize(maxsize)
	if not maxsize then return end
	local maxwidth, maxheight = unpack(explode('x', string.gsub(maxsize, 'px', '')))
	maxwidth, maxheight = tonumber(maxwidth), tonumber(maxheight)
	if maxwidth == 0 then maxwidth = nil end
	if maxheight == 0 then maxheight = nil end
	return maxwidth, maxheight
end

---Assemble the final wikicode for an image.
---@param imagename string
---@param link string
---@param text string
---@param size string As accepted by the `[[File:` syntax, e.g. `5x7px*0.75`.
---@param scale number This will be multiplied by the scale in `size`, if necessary.
---@param maxsize string
---@return string
local function imagecode(imagename, link, text, size, scale, maxsize)
	local image_output = '[[File:' .. imagename .. '|link='.. link .. '|' .. text
	if size or scale or maxsize then
		local basescale, width, height = parseSize(size) -- width, height: number or nil (basescale is string!)
		scale = (tonumber(scale) or 1) * (tonumber(basescale) or 1) -- combine the scale parameter and scale from the size parameter
		if scale == 0 or scale == 1 then
			scale = nil
		end
		local maxwidth, maxheight = parseMaxSize(maxsize)
		width, height = getImageSize(imagename, width, height, scale, maxwidth, maxheight) -- can be 0
		if width or height then
			image_output = image_output .. '|' .. (width or '') .. 'x' .. (height or '') .. 'px'
		end
	end
	return image_output .. ']]'
end

---Return the full `[[File:` wikicode for each image in the input (multiple are separated with `/`).
---@param image string
---@param link string
---@param text string
---@param size string
---@param scale string
---@param maxsize string
---@return string
local function images(image, link, text, size, scale, maxsize)

	if not image:find('/') then
		-- there is only one image in the input
		return imagecode(image, link, text, size, scale, maxsize)
	end

	-- there are multiple images in the input, separated with a slash
	image = explode('/', image)
	local result = ''
	if size and size:find('/') then
		-- there are multiple sizes in the size parameter
		size = explode('/', size) -- so turn it into a table
		for i, v in ipairs(image) do -- iterate over the images
			result = result .. imagecode(v, link, text, size[i], scale, maxsize) -- create the wikicode (using the respective size)
		end
	else
		for i, v in ipairs(image) do -- iterate over the images
			result = result .. imagecode(v, link, text, size, scale, maxsize) -- create the wikicode
		end
	end
	return result
end

---Return a string like `Internal Item ID: `, depending on the `_type`.
---@param _type '"item"'|'"tile"'|'"wall"'|'"npc"'|'"mount"'|'"buff"'|'"projectile"'|'"armor"'
---@return string
local function getIdText(_type)
	local id_text
	if _type == 'item' then -- a shortcut for faster
		id_text = l10n('id_text_item')
	elseif _type == 'tile' then
		id_text = l10n('id_text_tile')
	elseif _type == 'wall' then
		id_text = l10n('id_text_wall')
	elseif _type == 'npc' then
		id_text = l10n('id_text_npc')
	elseif _type == 'mount' then
		id_text = l10n('id_text_mount')
	elseif _type == 'buff' or _type == 'debuff' then
		id_text = l10n('id_text_buff')
	elseif _type == 'projectile' then
		id_text = l10n('id_text_projectile')
	elseif _type == 'armor' then
		id_text = l10n('id_text_armor')
	else
		id_text = l10n('id_text_item')
	end
	return id_text
end

-----------------------------------------------------------------
-- main return object
return {

parse = parse,
go = function(frame, args)
	-- cache?
	if not args then
		local cached = cache.get(':_item:' .. frame.args[1])
		if cached then
			return cached
		end
	end

	-- init var cache
	currentFrame = frame
	args_table = args or parse(frame.args[1])
	lang = getArg('lang') or 'en'
	l10n_table = l10n_info[lang] or l10n_info['en']

	local _arg1 = getArg(1) or ''
	local _nolink = getArg('nolink')
	local _link = _nolink and '' or getArg('link') or frame:expandTemplate{ title = 'tr', args = {_arg1, link='y', lang=lang} } -- now: _link == '' means nolink

	local text = getArg(2) or ''

	-- hovertext: {{tr|_arg1}} or text or _link (in that order)
	local hovertext
	if _arg1 ~= '' then
		hovertext = frame:expandTemplate{ title = 'tr', args = {_arg1, lang=lang} }
	elseif text ~= '' then
		hovertext = text
	else
		hovertext = _link
	end

	-- set output flags
	local output_image, output_text, output_table = true, true, false
	local _mode = getArg('mode')
	if _mode then
		if _mode == 'image' or _mode == 'imageonly' or _mode =='onlyimage' then
			output_text = false
		elseif _mode == 'text' or _mode == 'noimage' then
			output_image = false
		elseif _mode == 'table' or _mode == '2-cell' then
			output_table = true
		end
	end

	local class = 'i'

	local image_output, text_output
	-- get wikicode for the image(s)
	if output_image then
		local image_arg = getArg('image')
		if not image_arg then
			if _arg1 == '1/2 Second Timer' then
				image_arg = '1 2 Second Timer'
			elseif _arg1 == '1/4 Second Timer' then
				image_arg = '1 4 Second Timer'
			else
				image_arg = string.gsub(_arg1, ":%s*", " ")
			end
			image_arg = image_arg .. '.' .. (getArg('ext') or 'png')
		end
		if string.find(image_arg, '%[%[[fF]ile:') then
			image_output = '<span class="img">' .. image_arg .. '</span>'
		else
			image_output = images(image_arg, _link, hovertext, getArg('size'), getArg('scale'), getArg('maxsize'))
		end
	else
		image_output = ''
	end
	-- get wikicode for the text
	if output_text then
		local _note, _note2, _showid, _id, _icon = getArg('note'), getArg('note2'), getArg('showid'), getArg('id'), getArg('icons') -- get info from arguments

		-- prepare: display ID?
		if _id and not _showid then
			_showid = true
		end
		if _showid and (_showid == 'n' or _showid == 'no') then
			_showid = false
		end

		-- prepare: wrap?
		local _wrap
		if _showid or _note2 then
			_wrap = false
		else
			_wrap = getArg('wrap')
		end

		-- prepare: link and display text
		if _link ~= '' then
			if text == _link then
				text = '<span>[['..text..']]</span>'
			else
				text = '<span>[['.._link..'|'..text..']]</span>'
			end
		else
			text = '<span title="'..hovertext..'">'..text..'</span>'
		end

		-- prepare: eicons
		local icon = nil
		if _icon == 'n' or _icon == 'no' or _icon == 'off' then
			icon = ''
		else
			icon = eicons(getArg('epage') or _arg1, lang, (_showid or _note2 or _wrap or getArg('small')) and 'y')
		end

		-- assemble HTML code
		local content = text -- item name link text first.
		-- '-w' class means 'wrapmode', optimized for multiple lines of text. But it should be disabled for single line text.
		if _wrap then
			-- eicons in the same line
			if icon ~= '' then
				class = class .. ' -w'
				content = content .. icon
			end
			-- note in a new line
			if _note then
				class = class .. ' -w'
				content = content .. '<span class="note">' .. _note .. '</span>'
			end
		else
			-- note in the same line
			if _note then
				content = content .. '<span class="note">' .. _note .. '</span>'
			end
			-- eicons in the same line
			if icon ~= '' then
				content = content .. icon
			end
			-- note2 in a new line
			if _note2 then
				class = class .. ' -w'
				content = content .. '<div class="note">' .. _note2 .. '</div>'
			end
			-- id in a new line
			if _showid then
				class = class .. ' -w'
				local idtype = (getArg('type') or 'item'):lower()
				if not _id then
					-- get ID automatically via {{itemIdFromName}} or the like
					_id = frame:expandTemplate{ title = idtype .. 'IdFromName', args = {_arg1} }
				end
				local id_text = getIdText(idtype)
				content = content .. '<div class="id">' .. id_text .. _id .. '</div>'
			end
		end
		text_output = '<span>' .. content .. '</span>'
	else
		text_output = ''
	end

	-- handle custom CSS
	local _class, _css = getArg('class'), getArg('css')
	if _class then
		class = class .. ' ' .. _class -- add to existing classes
	end
	local attr = {class = class}
	if _css then
		attr.style = _css -- set the style attribute to parameter value
	end

	local return_string
	if output_table then
		-- table output
		attr.class = class
		local _rowspan = getArg('rowspan')
		local rowspan_text = (_rowspan and (' rowspan=' .. _rowspan) or '')
		-- prepare the two cells
		local first_cell_pre = rowspan_text .. ' class="il1c"'
		local first_cell_content = mw.text.tag('span', attr, image_output)
		local second_cell_pre = rowspan_text .. ' class="il2c"'
		local second_cell_content = mw.text.tag('span', attr, text_output)
		-- combine
		return_string = first_cell_pre .. " | " .. first_cell_content .. " || " .. second_cell_pre .. " | " .. second_cell_content
	else
		-- non-table output (text/image)
		return_string = mw.text.tag('span', attr, image_output .. text_output)
	end

	-- cache output for later reuse
	if not args and should_cache then
		cache.set(':_item:' .. frame.args[1], return_string, 3600*24) -- cache for 24 hours
	end

	-- output
	return return_string

end,

purge = function(frame)
	cache.delete(':_item:' .. frame.args[1]) -- delete that cache key.
end,

storeImageInfo = function(frame)
	currentFrame = frame
	local width, height = storeInfoToCargo(frame.args[1])
	if not width or width == 0 or not height or height == 0 then
		return
	else
		return frame:callParserFunction{name = "#dplvar:set", args = { 
			"_image_exist", "1",
			"_image_width", width,
			"_image_height", height,
		}}
	end
end,

}