Documentation for this module may be created at Module:Utility functions/doc
local util = {}
local type, pairs, ipairs = type, pairs, ipairs
function util.table_print(tt, indent, done)
done = done or {}
indent = indent or 0
if type(tt) == "table" then
local sb = {}
for key, value in pairs(tt) do
table.insert(sb, string.rep(" ", indent)) -- indent it
if type(value) == "table" and not done[value] then
done[value] = true
if type(key) ~= "number" then
table.insert(sb, string.format("%s = ", util.to_string(key)))
end
table.insert(sb, "{\n")
table.insert(sb, util.table_print(value, indent + 2, done))
table.insert(sb, string.rep(" ", indent)) -- indent it
table.insert(sb, "}\n")
elseif "number" == type(key) then
table.insert(sb, string.format("%s\n", util.to_string(value)))
else
table.insert(sb, string.format('%s = "%s"\n', util.to_string(key), util.to_string(value)))
end
end
return table.concat(sb)
else
return tt .. "\n"
end
end
function util.to_string(tbl)
if "nil" == type(tbl) then
return tostring("nil")
elseif "string" == type(tbl) then
return '"' .. tostring(tbl) .. '"'
elseif "table" == type(tbl) then
return util.table_print(tbl)
else
return tostring(tbl)
end
end
function util.trim(s)
-- from PiL2 20.4
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local function __genOrderedIndex(t)
local orderedIndex = {}
for key in pairs(t) do
table.insert(orderedIndex, key)
end
table.sort(
orderedIndex,
function(left, right)
if type(left) == "table" then
left = left[1]
end
if type(right) == "table" then
right = right[1]
end
return left < right
end
)
return orderedIndex
end
local function orderedNext(t, state)
-- Equivalent of the next function, but returns the keys in the alphabetic
-- order. We use a temporary ordered key table that is stored in the
-- table being iterated.
--print("orderedNext: state = "..tostring(state) )
if state == nil then
-- the first time, generate the index
t.__orderedIndex = __genOrderedIndex(t)
key = t.__orderedIndex[1]
return key, t[key]
end
-- fetch the next value
key = nil
for i = 1, #t.__orderedIndex do
if t.__orderedIndex[i] == state then
key = t.__orderedIndex[i + 1]
end
end
if key then
return key, t[key]
end
-- no more value to return, cleanup
t.__orderedIndex = nil
return
end
function util.orderedPairs(t)
-- Equivalent of the pairs() function on tables. Allows to iterate
-- in order
return orderedNext, t, nil
end
local langnames = nil
local code = function(title)
local subPage = (title or mw.title.getCurrentTitle()).subpageText:lower()
if not langNames then
langNames = require([[Module:Language/Names]])
end
if langNames[subPage] then
return subPage
end
end
function util.pageSuffix()
local langCode = code()
if langCode then
return "/" .. langCode
end
return "/en"
end
function util.compact(tab)
local keys = {}
local n = 0
for k, v in pairs(tab) do
n = n + 1
keys[n] = k
end
local out = {}
n = 0
for k, v in ipairs(keys) do
n = n + 1
out[n] = tab[v]
end
return out
end
-- this is exactly as lua-users wiki defined it. Never mind the odd gsub argument.
function util.interp(s, tab)
return (s:gsub(
"($%b{})",
function(w)
return tab[w:sub(3, -2)] or w
end
))
end
local function argOr(name, default)
local frame = mw.getCurrentFrame()
if frame == nil then
return default
end
return frame.args[name]
end
-- returns (true, module) or (false, message)
function util.requireDataLocalized(name)
-- So many ors because tilesheets somehow calls functions that call this *without a frame*
local forceUntranslated = argOr("forceUntranslated", false)
local moduleName = name
if not forceUntranslated then
moduleName = moduleName .. util.pageSuffix()
end
local success, data =
pcall(
function()
return mw.loadData(moduleName)
end
)
if not success then
success, data =
pcall(
function()
return mw.loadData(name)
end
)
end
return success, data
end
function util.wrapForInvoke(func)
return function(...)
local first = ...
local args = {}
if first == mw:getCurrentFrame() then
for k, v in pairs(first.args) do
args[k] = v
end
if first.args.fromParent then
for k, v in pairs(first:getParent().args) do
args[k] = v
end
end
else
args = {...}
end
return func(unpack(args))
end
end
function util.minetext(args)
local text = util.table_print(args or {{1}})
if type(args) == "string" then
text = args
else
args = args.args or args
text = args[1]
end
local blocktab = {
__call = function(left, right)
left.t[#left.t + 1] = right
end,
__index = {
reset = function(this)
this.fmt = {l = false, m = false, n = false, o = false}
this.c = false
end,
classes = function(this)
local c = {}
if this.c then
c = {"format-" .. tostring(this.c, 16)}
end
for k, v in pairs(this.fmt) do
if v then
c[#c + 1] = "format-" .. k
end
end
return table.concat(c, " ")
end,
text = function(this)
return table.concat(this.t, "")
end
}
}
local newblock = function(oldblock)
b = {
c = false,
t = {}
}
setmetatable(b, blocktab)
b:reset()
if oldblock then
for k, v in pairs(oldblock.fmt) do
b.fmt[k] = v
end
b.c = oldblock.c
end
return b
end
local escapes = {["#"] = ""}
for i, v in ipairs({"\\", "/", "&", "<", ">"}) do
escapes[v] = v
end
setmetatable(
escapes,
{__index = function(tab, key)
return "\\" + key
end}
)
local currblock = newblock()
local blocks = {currblock}
local state = "text" -- text, escape, format
for w in text:gmatch(".") do
if state == "escape" then
currblock(escapes[w])
state = "text"
elseif state == "text" then
if w == "\\" then
state = "escape"
elseif w == "&" then
state = "format"
else
currblock(w)
end
elseif state == "format" then
if w:match("[0-9a-f]") then
currblock = newblock()
blocks[#blocks + 1] = currblock
currblock.c = tonumber(w, 16)
elseif w:match("[l-o]") then
currblock = newblock(currblock)
blocks[#blocks + 1] = currblock
currblock.fmt[w] = not currblock.fmt[w]
else
currblock("&" .. w)
end
state = "text"
end
end
local outblocks = {}
for i, block in ipairs(blocks) do
local b = mw.html.create("span"):attr("class", block:classes()):wikitext(block:text())
outblocks[#outblocks + 1] = b
end
if args.nowrap then
local strings = {}
for i, block in ipairs(outblocks) do
strings[#strings + 1] = tostring(block)
end
return table.concat(strings, "")
else
local out = mw.html.create("span"):addClass("craftingGridText")
for i, block in ipairs(outblocks) do
out:node(block)
end
return tostring(out)
end
end
function util.join(frame)
local out = {}
for i, s in ipairs(frame.args) do
if not mw.ustring.match(s, "^%s*$") then
out[#out + 1] = s
end
end
return table.concat(out, frame.args.sep)
end
-- This function is CASE-senstive.
-- The title will be parsed and the first letter of the root page
-- will be capitalized automatically.
function util.hasSubPage(f)
local args = f or {}
if args == mw.getCurrentFrame() then
args = f.args or f:getParent().args
end
-- Trim whitespace
args = require( [[Module:ProcessArgs]] ).norm(args)
-- If we are on a language page, account for that
local title = mw.title.new(args[1] or "") or {}
local subpage = args[2]
local result = nil
if code(title) then
local frame = mw.getCurrentFrame()
local args = {title.fullText, 1, -2}
result = frame:callParserFunction("#titleparts", args) == subpage
else
result = title.subpageText == subpage
end
return result and 'true' or ''
end
return util