Module:Utils: Difference between revisions
From Eco - English Wiki
[unchecked revision] | [checked revision] |
Add 11 new utility functions. |
m Replace string library with mw.ustring in Demian's functions. |
||
Line 211: | Line 211: | ||
-- @param #number max Maximum value (right side) | -- @param #number max Maximum value (right side) | ||
-- @param #number default Default value in case of an error (only value). | -- @param #number default Default value in case of an error (only value). | ||
-- @param #string valueFormat Format string used with <code> | -- @param #string valueFormat Format string used with <code>mw.ustring.format</code>. | ||
-- @return #string "<code>min</code>–<code>max</code>" if <code>min < max</code> | -- @return #string "<code>min</code>–<code>max</code>" if <code>min < max</code> | ||
-- @return #string "<code>default</code>" formatted with <code>valueFormat</code> if <code>min == max</code> or <code>min > max</code> and <code>default ~= nil</code> | -- @return #string "<code>default</code>" formatted with <code>valueFormat</code> if <code>min == max</code> or <code>min > max</code> and <code>default ~= nil</code> | ||
Line 224: | Line 224: | ||
if nil ~= min and nil ~= max then | if nil ~= min and nil ~= max then | ||
if min < max then | if min < max then | ||
return | return mw.ustring.format(mw.ustring.format("%s–%s", valueFormat, valueFormat), min, max) | ||
elseif nil == default then | elseif nil == default then | ||
return nil | return nil | ||
else | else | ||
return | return mw.ustring.format(valueFormat, default) | ||
end | end | ||
end | end | ||
Line 278: | Line 278: | ||
local tbl = {} | local tbl = {} | ||
for token in | for token in mw.ustring.gmatch(str, mw.ustring.format("%%s?([^%s]+)%%s?", separator)) do | ||
table.insert(tbl, token) | table.insert(tbl, token) | ||
end | end | ||
Line 314: | Line 314: | ||
-- Try to get the actual end page instead of the disambiguation page if it exists. | -- Try to get the actual end page instead of the disambiguation page if it exists. | ||
-- E.g. Salmon has "Salmon (animal)" and "Salmon (item)" as well as the "Salmon" disambiguation page between these two. | -- E.g. Salmon has "Salmon (animal)" and "Salmon (item)" as well as the "Salmon" disambiguation page between these two. | ||
local directPage = | local directPage = mw.ustring.format("%s (%s)", name, disambiguationTitle) | ||
return mw.title.new(directPage).exists and directPage or name | return mw.title.new(directPage).exists and directPage or name | ||
end | end | ||
Line 329: | Line 329: | ||
if twoLineDisplayText then | if twoLineDisplayText then | ||
local lastSpaceIdx = | local lastSpaceIdx = mw.ustring.find(finalDisplayText, " [^ ]*$") | ||
if nil ~= lastSpaceIdx then | if nil ~= lastSpaceIdx then | ||
finalDisplayText = | finalDisplayText = mw.ustring.format("%s<br>%s", mw.ustring.sub(finalDisplayText, 0, lastSpaceIdx-1), mw.ustring.sub(finalDisplayText, lastSpaceIdx+1)) | ||
end | end | ||
end | end | ||
if pageName == finalDisplayText then | if pageName == finalDisplayText then | ||
return | return mw.ustring.format("[[%s]]", pageName) | ||
else | else | ||
return | return mw.ustring.format("[[%s|%s]]", pageName, finalDisplayText) | ||
end | end | ||
end | end | ||
return p | return p |
Revision as of 19:25, 24 February 2022
This module provides utility functions used from other modules.
Usage
Add the following line of code at the top of your file.
local Utils = require("Module:Utils")
-- You may then call functions from this module in your script. For example:
local tableLength = Utils.tableLen(myTable)
local p = {}
--- Trims and parses the args into a table, then returns the table
-- @author User:Avaren
function p.normaliseArgs(frame)
local origArgs = frame:getParent().args
local args = {}
for k, v in pairs(origArgs) do
v = mw.text.trim(tostring(v))
if v ~= '' then
args[k] = v
end
end
return args
end
--- Get path to icon file.
-- @author User:Avaren
function p.checkImage(name, too_expensive)
local icon = name:gsub('%s+', '') .. '_Icon.png'
if too_expensive then
return icon
end
if mw.title.makeTitle('File', icon).file.exists then
return icon
else
return 'NoImage.png'
end
end
--- Check if <code>item</code> is in given <code>array</code>.
-- @param item Item to look for
-- @param #table array Table to check
-- @return #bool <code>true</code> if <code>item</code> is in <code>array</code>
-- @author User:Avaren
local function in_array(item, array)
-- Should only use on short arrays
local set = {}
for _, l in ipairs(array) do
set[l] = true
end
return set[item] ~= nil
end
--- Build HTML code for an icon image.
-- @param name string
-- @param size string|nil One of: <code>"iconNormal"</code> (64px) or <code>"iconRecipe"</code> (44px). Default: <code>"iconNormal"</code>
-- @param bg string|nil
-- @param border string|nil
-- @param too_expensive boolean|nil
-- @author User:Avaren
function p.build_icon(name, link, size, bg, border, too_expensive)
local L = require('Module:Localization') -- local import
if not size then
size = 'iconNormal'
end
local icon_bg
if bg then
icon_bg = bg
end
local icon_border
if border then
icon_border = border
end
local item_data = mw.loadData('Module:ItemData')
local item = item_data.items[name]
local image
if item then
if item['group'] == L.t('Skill Books') then
image = 'SkillBook.png'
icon_bg = 'iconGold'
elseif item['group'] == L.t('Skill Scrolls') then
image = 'Skill Scroll'
icon_bg = 'iconGold'
-- Attempt to generate skill page
elseif in_array(L.t('Basic Research'), item['tagGroups']) then
image = string.sub(item['untranslated'], 1, -7):gsub('%s+', '') .. '_Icon.png'
icon_bg = 'paperBasic'
elseif in_array(L.t('Advanced Research'), item['tagGroups']) then
image = string.sub(item['untranslated'], 1, -10):gsub('%s+', '') .. '_Icon.png'
icon_bg = 'paperAdvanced'
elseif in_array(L.t('Modern Research'), item['tagGroups']) then
image = string.sub(item['untranslated'], 1, -8):gsub('%s+', '') .. '_Icon.png'
icon_bg = 'paperModern'
else
image = p.checkImage(item['untranslated'], too_expensive)
end
if not icon_bg then
if item['group'] == L.t('Food') then
icon_bg = 'iconGreen'
elseif item['carried'] == L.t('Hands') then
icon_bg = 'iconBrown'
end
end
else
image = p.checkImage(name, too_expensive)
end
if not icon_bg then
icon_bg = 'iconBlue'
end
if border then
icon_border = border
else
icon_border = 'borderBlue'
end
if size == 'iconNormal' then
icon_container = 'iconContainer'
else
icon_container = 'iconContainerSmall'
end
if not link then
link = ''
else
link = '|link='..link
end
local file = '[[File:' .. image .. '|frameless|class=' .. size .. ' ' .. icon_bg .. link ..']]'
return '<div class="' .. icon_container .. '"><div class="iconStack">' .. file .. '</div><div class="iconBorder ' .. icon_border .. '" style="position:absolute;"></div></div>'
end
--- Get HTML code for an icon image.
-- @author User:Avaren
function p.Icon(frame)
args = p.normaliseArgs(frame)
return p.build_icon(args.name, args.link, args.size, args.bg, args.border, args.too_expensive)
end
--- Calculate the length of a table by iterating over every item in it.
--
-- <code>mw.LoadData</code> prevents <code>#tbl</code> from working correctly.
-- @param #table tbl Table to calculate the length of
-- @return #number Length of the table.
-- @author User:Avaren
function p.tableLen(tbl)
local count = 0
for _, v in ipairs(tbl) do
if v == nil then
return count
end
count = count + 1
end
return count
end
--- Check if <code>value</code> is not <code>nil</code> and return it or if it is <code>nil</code> fall back to <code>default</code>.
-- @param value Value to check
-- @param default Value to fall back to
-- @return <code>value</code> if it is not <code>nil</code>
-- @return <code>default</code> if <code>value</code> is <code>nil</code>
-- @author User:Demian
-- @see valueOrDash
-- @see formatNilToYesNo
-- @see formatBoolToYesNo
function p.valueOrDefault(value, default)
return nil == value and default or value
end
--- Check if <code>value</code> is not <code>nil</code> and return it or if it is <code>nil</code> fall back to the em-dash (—).
--
-- The em-dash (—) is commonly used represent a missing, not applicable (N/A), or a negative ("no") value with just a single character.
-- @param value Value to check
-- @return <code>value</code> if it is not <code>nil</code>
-- @return #string "—" if <code>value</code> is <code>nil</code>
-- @author User:Demian
-- @see valueOrDefault
function p.valueOrDash(value)
return nil == value and "—" or value
end
--- Check if <code>value</code> is not <code>nil</code> and return "Yes" or "No".
-- @param value Value to check
-- @return #string "Yes" if <code>value</code> is not <code>nil</code>
-- @return #string "No" if <code>value</code> is <code>nil</code>
-- @author User:Demian
-- @see valueOrDefault
-- @usage formatNilToYesNo("Hello") == "Yes"
-- @usage formatNilToYesNo(nil) == "No"
function p.formatNilToYesNo(value)
-- TODO: Support i18n.
return nil == value and "No" or "Yes"
end
--- Check if <code>value</code> <em>evaluates</em> as <code>true</code> and return "Yes" or "No".
-- @param value Value to evaluate. Does not have to be a bool.
-- @return #string "Yes" if <code>value</code> evaluates as <code>true</code>
-- @return #string "No" if <code>value</code> evaluates as <code>false</code>
-- @author User:Demian
-- @see valueOrDefault
-- @usage formatBoolToYesNo("") == true
-- @usage formatBoolToYesNo(123) == true
-- @usage formatBoolToYesNo(nil) == false
function p.formatBoolToYesNo(value)
-- TODO: Support i18n.
return value and "Yes" or "No"
end
--- Format the input values into a string representing the range between the values.
--
-- Returning an an empty string intended to ease concatenation with other strings.
-- The en-dash (–) (instead of the hyphen-minus "-") is the appropriate character to signify a range of values.
-- @param #number min Minimum value (left side)
-- @param #number max Maximum value (right side)
-- @param #number default Default value in case of an error (only value).
-- @param #string valueFormat Format string used with <code>mw.ustring.format</code>.
-- @return #string "<code>min</code>–<code>max</code>" if <code>min < max</code>
-- @return #string "<code>default</code>" formatted with <code>valueFormat</code> if <code>min == max</code> or <code>min > max</code> and <code>default ~= nil</code>
-- @return #string "" (empty string) if either <code>min</code> <strong>or</strong> <code>max</code> do not convert to a numerical value
-- @return #nil <code>nil</code> if <code>min == max</code> or <code>min > max</code> and <code>default == nil</code>
-- @author User:Demian
function p.toRangeString(min, max, default, valueFormat)
min = tonumber(p.valueOrDefault(min, nil))
max = tonumber(p.valueOrDefault(max, nil))
default = tonumber(p.valueOrDefault(default, nil))
if nil ~= min and nil ~= max then
if min < max then
return mw.ustring.format(mw.ustring.format("%s–%s", valueFormat, valueFormat), min, max)
elseif nil == default then
return nil
else
return mw.ustring.format(valueFormat, default)
end
end
return ""
end
--- Get all keys from <code>tbl</code> and sort them in alphabetical order.
-- @param #table tbl Table to get keys from
-- @return #table Input table keys in alphabetical order.
-- @author User:Demian
-- @see getSortedValues
function p.getSortedKeys(tbl)
local sorted = {}
for key in pairs(tbl) do
table.insert(sorted, key)
end
table.sort(sorted)
return sorted
end
--- Get all values from <code>tbl</code> and sort them in alphabetical order.
-- @param #table tbl Table to get values from
-- @return #table Input table values in alphabetical order.
-- @author User:Demian
-- @see getSortedKeys
function p.getSortedValues(tbl)
local sorted = {}
for _, value in ipairs(tbl) do
table.insert(sorted, value)
end
table.sort(sorted)
return sorted
end
--- Split <code>str</code> by the given character.
-- @param #string str String to split
-- @param #string separator String that separates values in <code>str</code>. May optionally be surrounded by 1 <em>whitespace</em> character by default.
-- @return #table Table of strings that were split from <code>str</code>.
-- @author User:Demian
-- @usage splitString("hello, world", ",") == {"hello", "world"}
function p.splitString(str, separator)
local tbl = {}
for token in mw.ustring.gmatch(str, mw.ustring.format("%%s?([^%s]+)%%s?", separator)) do
table.insert(tbl, token)
end
return tbl
end
--- Sort items in the given list of values <code>str</code> separated with <code>separator</code> and return them as a single string.
-- @param #string str String with values separated by <code>separator</code>
-- @param #string separator String that separates values in <code>str</code>
-- @param #string joiner String used to join sorted values from <code>str</code>.
-- @return #string <code>str</code> with items sorted in alphabetical order.
-- @author User:Demian
-- @usage sortListString("Dog,Ape, Cat", ",", ";") == "Ape;Cat;Dog"
function p.sortListString(str, separator, joiner)
-- Split string by commas.
-- Sort items.
-- Rejoin into string.
return table.concat(p.getSortedValues(p.splitString(str, separator)), joiner)
end
--- Check if a page with the title "<code>name</code> (<code>disambiguationTitle</code>)" exists in the database and return that page title, otherwise return "<code>name</code>".
--
-- Use sparingly as this uses a comparatively slow MediaWiki function p.to check if a page exists.
--
-- Using this function p.will create a new entry in the <code>Special:WantedPages</code> list.
-- Be careful when calling this function p.and do not pass garbage into its parameters so you do not clog up that list.
-- This is a long-standing issue with MediaWiki that has not yet been solved, and may not be possible to solve without an architectural change to the software.
-- @param #string name Name of a page.
-- @param #string disambiguationTitle Disambiguation clarifier in a page title.
-- @return #string "<code>name</code> (<code>disambiguationTitle</code>)"
-- @return #string "<code>name</code>"
-- @author User:Demian
function p.getDirectPageName(name, disambiguationTitle)
-- Try to get the actual end page instead of the disambiguation page if it exists.
-- E.g. Salmon has "Salmon (animal)" and "Salmon (item)" as well as the "Salmon" disambiguation page between these two.
local directPage = mw.ustring.format("%s (%s)", name, disambiguationTitle)
return mw.title.new(directPage).exists and directPage or name
end
--- Create a wikilink with [[square brackets]] from parameters.
-- @param #string pageName The actual name of a page to create a link to
-- @param #string displayText Text to display as a clickable link instead of the page name. If <code>nil</code>, <code>pageName</code> is displayed instead.
-- @param #bool twoLineDisplayText Force the <em>last word</em> of <code>displayText</code> on the next line
-- @return #string "[[<code>name</code>|<code>displayText</code>]]" if <code>displayText</code> is not <code>nil</code>
-- @return #string "[[<code>name</code>]]" if <code>displayText</code> is <code>nil</code> or the same string as <code>name</code>
-- @author User:Demian
function p.formatWikilink(pageName, displayText, twoLineDisplayText)
local finalDisplayText = p.valueOrDefault(displayText, pageName)
if twoLineDisplayText then
local lastSpaceIdx = mw.ustring.find(finalDisplayText, " [^ ]*$")
if nil ~= lastSpaceIdx then
finalDisplayText = mw.ustring.format("%s<br>%s", mw.ustring.sub(finalDisplayText, 0, lastSpaceIdx-1), mw.ustring.sub(finalDisplayText, lastSpaceIdx+1))
end
end
if pageName == finalDisplayText then
return mw.ustring.format("[[%s]]", pageName)
else
return mw.ustring.format("[[%s|%s]]", pageName, finalDisplayText)
end
end
return p