Module:Utility functions

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 ) 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 = {["#"] = "&#23;"} 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