Module:Test
Jump to navigation
Jump to search
A test module to avoid disturbing "real" pages.
See Terraria Wiki:Lua for more information about Lua and links to other help resources.
Feel free to test this module at Terraria Wiki:Sandbox.
---Holds the tables with the l10n information for the different languages, taken from the l10n submodule.
local l10nInfo = mw.loadData('Module:Item/l10n')
---Holds the l10n information for the current language, as key-value pairs.
local l10nTable
---The current language. Determines which l10n table to use.
local lang
---Holds the arguments from the template call.
local args
local shouldCache = true
local options, optionsSnap = (function()
local options, snap = {}, ''
local op = require('Module:Options')
for _,key in ipairs(op.getAllKeys('item')) do
local v = op.get('item', key)
snap = snap..v..'∥'
if v ~= '' then
options[key] = v
end
end
return options, snap
end)()
local trim = mw.text.trim
local cargo = mw.ext.cargo
local cache = mw.ext.LuaCache
local eicons = require('Module:Exclusive').simpleEicons
local tr = require('Module:Tr')
-- Note:
-- for getArg('x', f()), f() will always be parsed;
-- for getArg('x') or f(), f() will only be parsed when used.
local function getArg(key, defaultForEmpty, defaultForBlank)
local value = args[key]
if not value then -- there is no |key=xxx
return defaultForEmpty
elseif value == '' then -- explicit |key=|
if defaultForBlank == nil then
return defaultForEmpty
else
return defaultForBlank
end
else
return value
end
end
local md5 = function(str)
return mw.hash.hashValue('md5', str)
end
local function pairsByKeys(t, f)
local a = {}
for n in pairs(t) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], t[a[i]]
end
end
return iter
end
---@param value string
---@param default bool
---@return bool
local function bool(value, default)
if value then
value = string.lower(tostring(value))
if value == 'y' or value == 'yes' or value == '1' or value == 'true' or value == 'on' then
return true
elseif value == 'n' or value == 'no' or value == '0' or value == 'false' or value == 'off' then
return false
end
return default
end
end
---Return the l10n string associated with the `key`.
---@param key string
---@return string
local function l10n(key)
return l10nTable[key] or l10nInfo['en'][key]
end
local function getCacheKey(arg, lang)
local arr = { lang, optionsSnap }
for k,v in pairsByKeys(arg) do
arr[#arr+1] = k..':'..v
end
return table.concat(arr, '※');
end
---Split the `str` on each `div` in it and return the result as a table. It can be over 60x faster then mw.text.split
---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', {
-- md5name for case-sensitive query.
where = 'md5name='.. "'"..md5(imagename).."'",
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.
shouldCache = 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
shouldCache = true -- ok, cache it.
mw.getCurrentFrame:callParserFunction('#cargo_store:_table=Imageinfo',{
image = imagename,
md5name = md5(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 imageOutput = '[[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
imageOutput = imageOutput .. '|' .. (width or '') .. 'x' .. (height or '') .. 'px'
end
end
return imageOutput .. ']]'
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 {
go = function(frame, inputArgs)
args = inputArgs or frame:getParent().args
lang = getArg('lang') or require('Module:Lang').get() or 'en'
-- cache?
local cache_key = getCacheKey(args, lang)
local cached = cache.get(cache_key)
if cached then
return cached
end
l10nTable = l10nInfo[lang] or l10nInfo['en']
local name = trim(getArg(1, ''))
if name == '' then
name = frame:expandTemplate{ title = (getArg('type') or options.type)..'NameFromId', args = {getArg('id'), lang='en'} }
end
local text = getArg('t')
if not text then
local t = trim(getArg(2, ''))
if t == '' then
text = tr.translate(name, lang)
else
text = frame:expandTemplate{ title = 'displaytext', args = {name, t, lang=lang} }
end
end
local nolink = bool(getArg('nolink') or options.nolink)
local link = nolink and '' or getArg('link', nil, '') or tr.translateLink(name, lang) -- now: link == '' means nolink
-- set output flags
local outputImage, outputText, outputTable = true, true, false
local mode = getArg('mode') or options.mode
if mode then
if mode == 'image' or mode == 'imageonly' or mode =='onlyimage' then
outputText = false
elseif mode == 'text' or mode == 'noimage' then
outputImage = false
elseif mode == 'table' or mode == '2-cell' then
outputTable = true
end
end
local hovertext
if outputImage and not outputText then
-- with image only, the hovertext will only be displayed on the image, so it should be text or {{tr|name}} or link (in that order)
if text ~= '' then
hovertext = text
elseif name ~= '' then
hovertext = tr.translate(name, lang)
else
hovertext = link
end
else
-- with image and/or text, the hovertext will be displayed on the image and on the text, so it should be {{tr|name}} or text or link (in that order)
if name ~= '' then
hovertext = tr.translate(name, lang)
elseif text ~= '' then
hovertext = text
else
hovertext = link
end
end
local class = 'i'
local imageOutput, textOutput
-- wikicode for the image(s)
if outputImage then
local imageArg = getArg('image') or string.gsub(name, "[:/]%s*", " ")..'.'..getArg('ext', 'png')
if string.find(imageArg, '%[%[[fF]ile:') then
imageOutput = '<span class="img">' .. imageArg .. '</span>'
else
imageOutput = images(imageArg, link, hovertext, getArg('size'), getArg('scale') or options.scale, getArg('maxsize') or options.maxsize)
end
else
imageOutput = ''
end
-- wikicode for the text
if outputText then
local note, note2, bignote = getArg('note'), getArg('note2'), getArg('bignote')
local id = getArg('id')
local showid = bool(getArg('showid') or options.showid, id)
-- prepare: wrap?
local wrap
if showid or note2 then
wrap = false
else
wrap = bool(getArg('wrap') or options.wrap)
end
-- prepare: eicons
local iconstr = ''
if text ~= '' then -- no display text no eicons.
local icons = getArg('icons') or options.icons
icons = bool(icons, icons)
if icons then
local small = bool((showid or note2 or wrap) and 'y' or getArg('small') or options.small)
if icons == true then
iconstr = eicons(name, lang, small)
else
-- make the size of {{eicons}} from {{{icon}}} input match {{{small}}} setting.
if small then
iconstr = string.gsub(icons, ' class="eico ', ' class="eico s ')
else
iconstr = string.gsub(icons, ' class="eico s ', ' class="eico ')
end
end
end
end
-- prepare: link and display text
if link == '' or text == '' or string.find(text, '%[%[.-%]%]') then
text = '<span title="'..hovertext..'">'..text..'</span>'
else
if text == link then
text = '<span>[['..text..']]</span>'
else
text = '<span>[['..link..'|'..text..']]</span>'
end
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.
local wrapclass = false
if wrap then
-- eicons in the same line
if iconstr ~= '' then
wrapclass = true
content = content .. iconstr
end
-- note in a new line
if note then
wrapclass = true
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 iconstr ~= '' then
content = content .. iconstr
end
-- note2 in a new line
if note2 then
wrapclass = true
content = content .. '<span class="note2">' .. note2 .. '</span>'
end
-- id in a new line
if showid then
wrapclass = true
local idType = (getArg('type') or options.type or 'item'):lower()
if not id then
-- get ID automatically via {{itemIdFromName}} or the like
id = frame:expandTemplate{ title = idType .. 'IdFromName', args = {name} }
end
local idText = getIdText(idType)
content = content .. '<span class="id">' .. idText .. id .. '</span>'
end
end
if wrapclass then
class = class .. ' -w'
end
if bignote then
textOutput = '<span>' .. content .. '</span><span>' .. bignote .. '</span>'
else
textOutput = '<span>' .. content .. '</span>'
end
else
textOutput = ''
end
-- handle custom CSS
local inputClass, inputCss = getArg('class') or options.class, getArg('css') or options.css
if inputClass then
class = class .. ' ' .. inputClass -- add to existing classes
end
local attr = {class = class}
if inputCss then
attr.style = inputCss -- set the style attribute to parameter value
end
-- anchor:
local _anchor = getArg('anchor')
local return_string
if outputTable 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, imageOutput)
if _anchor then
first_cell_content = _anchor .. first_cell_content
end
local second_cell_pre = rowspan_text .. ' class="il2c"'
local second_cell_content = mw.text.tag('span', attr, textOutput)
-- 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, _anchor and (imageOutput .. textOutput .. _anchor) or (imageOutput .. textOutput) )
end
-- cache output for reuse
if shouldCache then
cache.set(cache_key, 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)
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,
}