mNo edit summary |
(Cleanup return value) |
||
(37 intermediate revisions by 7 users not shown) | |||
Line 1: | Line 1: | ||
− | local |
+ | local util = {} |
+ | local type, pairs, ipairs = type, pairs, ipairs |
||
− | local util = require("Module:Utility_functions") |
||
+ | function util.table_print(tt, indent, done) |
||
− | -- returns (true, module) or (false, message) |
||
+ | done = done or {} |
||
− | function loadTranslatedModule(name, forceUntranslated) |
||
− | + | indent = indent or 0 |
|
− | if |
+ | 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 |
||
− | return pcall(function() |
||
− | return require(moduleName) |
||
− | end) |
||
end |
end |
||
+ | function util.to_string(tbl) |
||
− | local stringsLoaded, strings = loadTranslatedModule("Module:Infobox/strings") |
||
− | if |
+ | if "nil" == type(tbl) then |
+ | return tostring("nil") |
||
− | strings = {} |
||
+ | elseif "string" == type(tbl) then |
||
+ | return '"' .. tostring(tbl) .. '"' |
||
+ | elseif "table" == type(tbl) then |
||
+ | return util.table_print(tbl) |
||
+ | else |
||
+ | return tostring(tbl) |
||
+ | end |
||
end |
end |
||
+ | |||
− | setmetatable(strings, {__index = function(tab, key) |
||
+ | function util.trim(s) |
||
− | if (key == "_fellbackStrings") then |
||
+ | -- from PiL2 20.4 |
||
− | tab._fellbackStrings = {} |
||
− | + | return (s:gsub("^%s*(.-)%s*$", "%1")) |
|
+ | end |
||
+ | |||
+ | local function __genOrderedIndex(t) |
||
+ | local orderedIndex = {} |
||
+ | for key in pairs(t) do |
||
+ | table.insert(orderedIndex, key) |
||
end |
end |
||
− | + | table.sort( |
|
+ | orderedIndex, |
||
− | if (key == "_missingStrings") then |
||
− | + | function(left, right) |
|
+ | if type(left) == "table" then |
||
− | return tab._missingStrings |
||
+ | left = left[1] |
||
− | end |
||
− | + | end |
|
− | if |
+ | if type(right) == "table" then |
+ | right = right[1] |
||
− | if rawget(tab,"_stringTableEntirelyMissing") then |
||
+ | end |
||
− | return "!!! [[Module:Infobox/strings]] is gone or not a valid Lua module !!!" |
||
− | + | return left < right |
|
− | if (#tab._fellbackStrings > 0) or (#tab._missingStrings > 0) then |
||
− | return "!!! Problem with [[Module:Infobox/strings]] or [[Module:Infobox/strings".. util.pageSuffix() .."|a translation thereof]]. Fallbacks used for: {" .. table.concat(tab._fellbackStrings, ", ") .. "}. Missing strings: {" .. table.concat(tab._missingStrings, ", ") .. "}. Please inform a translation admin as the translation markings probably need updating. !!!" |
||
− | else |
||
− | return nil |
||
end |
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 |
end |
||
+ | -- fetch the next value |
||
− | |||
+ | key = nil |
||
− | if not rawget(tab,"_fallback") then |
||
− | + | for i = 1, #t.__orderedIndex do |
|
+ | if t.__orderedIndex[i] == state then |
||
− | tab._fallback = require("Module:Infobox/strings") |
||
+ | key = t.__orderedIndex[i + 1] |
||
− | end) |
||
− | if not rawget(tab,"_fallback") then |
||
− | tab._stringTableEntirelyMissing = true |
||
− | tab._fallback = { } |
||
end |
end |
||
end |
end |
||
+ | |||
− | |||
+ | if key then |
||
− | local val = tab._fallback[key] |
||
+ | return key, t[key] |
||
− | if val then |
||
− | tab._fellbackStrings[#(tab._fellbackStrings)+1] = key |
||
− | else |
||
− | tab._missingStrings[#(tab._missingStrings)+1] = key |
||
− | val = "noString!"..key |
||
end |
end |
||
− | val = string.gsub(val, "</?translate>","") |
||
− | tab[key] = val -- inhibit repeats. |
||
− | return val |
||
− | end}) |
||
+ | -- no more value to return, cleanup |
||
− | local function makePreprocess(frame) |
||
+ | t.__orderedIndex = nil |
||
− | return function(str) |
||
− | + | return |
|
− | return frame:preprocess(tostring(str)) or "wat" |
||
− | else |
||
− | return "" |
||
− | end |
||
− | end |
||
end |
end |
||
+ | function util.orderedPairs(t) |
||
− | -- load an infobox |
||
+ | -- Equivalent of the pairs() function on tables. Allows to iterate |
||
− | local function loadInfobox(name, forceUntranslated) |
||
+ | -- in order |
||
− | return loadTranslatedModule("Module:Infobox/" .. util.trim(name), forceUntranslated) |
||
+ | return orderedNext, t, nil |
||
end |
end |
||
+ | local langnames = nil |
||
− | -- this is exactly as lua-users wiki defined it. Never mind the odd gsub argument. |
||
− | + | local code = function(title) |
|
+ | local subPage = (title or mw.title.getCurrentTitle()).subpageText:lower() |
||
− | return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end)) |
||
+ | if not langNames then |
||
+ | langNames = require([[Module:Language/Names]]) |
||
end |
end |
||
+ | if langNames[subPage] then |
||
− | |||
+ | return subPage |
||
− | -- return the canonical, ie first, name given in an argdata. |
||
− | local function argCName(argdata) |
||
− | if type(argdata.arg) == "string" then |
||
− | return argdata.arg |
||
− | else |
||
− | return argdata.arg[1] |
||
end |
end |
||
end |
end |
||
+ | function util.pageSuffix() |
||
− | |||
+ | local langCode = code() |
||
− | -- wrap an argument value in the necessary surround |
||
+ | if langCode then |
||
− | local function wrapArg(argdata, value) |
||
− | + | return "/" .. langCode |
|
− | local sizeRequested = tonumber(argdata.maxSize or "0") |
||
− | if sizeRequested == 0 then |
||
− | sizeRequested = tonumber(strings.maxImageSize) |
||
− | end |
||
− | |||
− | local parameters = (argdata.imageParameters or strings.imageParameters) |
||
− | if parameters == "" then |
||
− | parameters = nil |
||
− | end |
||
− | mw.log({'File:'.. value, sizeRequested .. 'px', parameters}) |
||
− | |||
− | -- force frameless for now, because that makes size do the right thing |
||
− | local stuff = util.compact({'File:'.. value, sizeRequested .. 'px', parameters , "frameless"}) |
||
− | return '[['.. table.concat(stuff,"|")..']]' |
||
− | else |
||
− | return value |
||
end |
end |
||
+ | return "/en" |
||
end |
end |
||
+ | function util.compact(tab) |
||
− | -- resolve argument data for a given arg table |
||
+ | local keys = {} |
||
− | local function argtableResolver(argdata, argtable, errordata) |
||
− | + | local n = 0 |
|
− | + | for k, v in pairs(tab) do |
|
+ | n = n + 1 |
||
+ | keys[n] = k |
||
end |
end |
||
− | + | local out = {} |
|
+ | n = 0 |
||
− | if type(argdata) == "string" then |
||
− | + | for k, v in ipairs(keys) do |
|
+ | n = n + 1 |
||
+ | out[n] = tab[v] |
||
end |
end |
||
+ | return out |
||
+ | end |
||
+ | -- this is exactly as lua-users wiki defined it. Never mind the odd gsub argument. |
||
− | local text = nil |
||
+ | function util.interp(s, tab) |
||
− | if type(argdata.arg) == "string" then |
||
+ | return (s:gsub( |
||
− | text = argtable[argdata.arg] |
||
+ | "($%b{})", |
||
− | elseif (type(argdata.arg) == "table") and (#(argdata.arg) > 0) then |
||
− | + | function(w) |
|
− | + | return tab[w:sub(3, -2)] or w |
|
− | if not (candidate == "") then |
||
− | text = candidate |
||
− | end |
||
end |
end |
||
− | + | )) |
|
− | error(strings.errArgMissingArgName .. " " .. interp(position.formatString, errordata)) |
||
− | end |
||
− | |||
− | if not text then |
||
− | return nil |
||
− | end |
||
− | |||
− | if argdata.type == "switch" then |
||
− | if (not argdata.allowedValues) or (type(argdata.allowedValues) ~= "table") then |
||
− | error(strings.errArgMissingSwitchValues .. " " .. interp(position.formatString, errordata)) |
||
− | elseif not argdata.allowedValues[text] then |
||
− | text = strings.unknownType .. ' [[Category:Articles with bad parameter values]]' |
||
− | else |
||
− | text = argdata.allowedValues[text] |
||
− | end |
||
− | end |
||
− | |||
− | if argdata.style then |
||
− | return {style=argdata.style, wrapArg(argdata, text)} |
||
− | else |
||
− | return wrapArg(argdata, text) |
||
− | end |
||
end |
end |
||
− | local function |
+ | local function argOr(name, default) |
+ | local frame = mw.getCurrentFrame() |
||
− | sourceframe = frame |
||
− | if frame |
+ | if frame == nil then |
− | + | return default |
|
− | end |
||
− | return function(argdata, errordata) |
||
− | return argtableResolver(argdata, sourceframe.args, errordata) |
||
end |
end |
||
+ | return frame.args[name] |
||
end |
end |
||
+ | -- returns (true, module) or (false, message) |
||
− | -- resolve argument data for an empty infobox |
||
+ | function util.requireDataLocalized(name) |
||
− | -- parameters |
||
+ | -- So many ors because tilesheets somehow calls functions that call this *without a frame* |
||
− | -- argdata: argdata|string |
||
+ | local forceUntranslated = argOr("forceUntranslated", false) |
||
− | -- returns |
||
+ | local moduleName = name |
||
− | -- string|{style=cellstyle, string} |
||
+ | if not forceUntranslated then |
||
− | local function emptyBoxResolver(argdata) |
||
+ | moduleName = moduleName .. util.pageSuffix() |
||
− | if not argdata then |
||
− | return "wat" |
||
end |
end |
||
+ | |||
− | |||
+ | local success, data = |
||
− | if type(argdata) == "string" then |
||
− | + | pcall( |
|
+ | function() |
||
− | elseif type(argdata) == "table" then |
||
− | + | return mw.loadData(moduleName) |
|
− | if argdata.style then |
||
− | return {style=argdata.style, wrapArg(argdata, text)} |
||
− | else |
||
− | return wrapArg(argdata, text) |
||
end |
end |
||
+ | ) |
||
+ | |||
+ | if not success then |
||
+ | success, data = |
||
+ | pcall( |
||
+ | function() |
||
+ | return mw.loadData(name) |
||
+ | end |
||
+ | ) |
||
end |
end |
||
+ | |||
+ | return success, data |
||
end |
end |
||
+ | function util.wrapForInvoke(func) |
||
− | -- resolve the arguments of an infobox definition according to the given resolver, |
||
+ | return function(...) |
||
− | -- and return a structure with all the arguments resolved and any unnecessary sections |
||
+ | local first = ... |
||
− | -- etc omitted (sections with an argument title are never omitted). |
||
+ | local args = {} |
||
− | -- parameters |
||
+ | if first == mw:getCurrentFrame() then |
||
− | -- args.box: infobox definition |
||
+ | for k, v in pairs(first.args) do |
||
− | -- args.resolver: function(argdata|string) -> string. |
||
+ | args[k] = v |
||
− | local function calculateEffectiveInfobox(args) |
||
− | local effectiveBox = {type="box"} |
||
− | |||
− | if (not args.box) or (type(args.box) ~= "table") then |
||
− | error(strings.errNoFormat) |
||
− | end |
||
− | |||
− | local sectioncount = 0 |
||
− | for i, section in ipairs(args.box) do |
||
− | if type(section) ~= "table" then |
||
− | error(interp(strings.errBadSection, {section = i})) |
||
− | end |
||
− | |||
− | if not section.docOnly then |
||
− | sectioncount = sectioncount + 1 |
||
− | |||
− | if not section.title then |
||
− | error(interp(strings.errSectionNoTitle, {section = i})) |
||
end |
end |
||
− | + | if first.args.fromParent then |
|
+ | for k, v in pairs(first:getParent().args) do |
||
− | local newSection = { title = args.resolver(section.title, {formatString = strings.errSectionLocation, section = i}), type="section" } |
||
− | + | args[k] = v |
|
− | for j, subsection in ipairs(section) do |
||
− | if type(subsection) ~= "table" then |
||
− | error(interp(strings.errBadSubsection, {section = i, subsection = j})) |
||
− | end |
||
− | if not subsection.docOnly then |
||
− | local newSubsection = {type="subsection"} |
||
− | |||
− | for k, row in ipairs(subsection) do |
||
− | if type(row) ~= "table" then |
||
− | error(interp(strings.errBadRow, {section = i, subsection = j, row = k})) |
||
− | end |
||
− | if not row.docOnly then |
||
− | local newRow = { type="row"} |
||
− | |||
− | for m, cell in ipairs(row) do |
||
− | newRow[#newRow + 1] = args.resolver(cell, {formatString = strings.errCellLocation, section = i, subsection = j, row = k, cell = m}) |
||
− | end |
||
− | |||
− | if (#row == #newRow) then |
||
− | newSubsection[#newSubsection+1] = newRow |
||
− | end |
||
− | end |
||
− | end |
||
− | |||
− | if (#newSubsection > 0) then |
||
− | newSection[#newSection + 1] = newSubsection |
||
− | end |
||
end |
end |
||
end |
end |
||
− | + | else |
|
+ | args = {...} |
||
− | if (#newSection > 0) or (type(section.title) == "table") then |
||
− | effectiveBox[#effectiveBox + 1] = newSection |
||
− | end |
||
end |
end |
||
+ | return func(unpack(args)) |
||
end |
end |
||
− | |||
− | if sectioncount == 0 then |
||
− | error(strings.errEmptyInfoboxFormat) |
||
− | end |
||
− | |||
− | return effectiveBox |
||
end |
end |
||
+ | function util.minetext(args) |
||
− | -- convert an infobox definition that has had all the arguments resolved, to a string. |
||
+ | local text = util.table_print(args or {{1}}) |
||
− | -- box like |
||
+ | if type(args) == "string" then |
||
− | -- { class=tableclass, { title=sectionheader, { { cell, cell }, {cell, cell } } } } |
||
+ | text = args |
||
− | -- cell like string or { style=cellstyle, content } |
||
+ | else |
||
− | local function renderBox(box, wtprocessor) |
||
− | + | args = args.args or args |
|
− | + | text = args[1] |
|
− | result = result .. thing |
||
end |
end |
||
+ | local blocktab = { |
||
− | out('<table class="infobox') |
||
+ | __call = function(left, right) |
||
− | if box.class then |
||
− | + | left.t[#left.t + 1] = right |
|
− | end |
+ | end, |
− | + | __index = { |
|
+ | reset = function(this) |
||
− | |||
+ | this.fmt = {l = false, m = false, n = false, o = false} |
||
− | local count = 0 |
||
+ | this.c = false |
||
− | |||
+ | end, |
||
− | for i,section in ipairs(box) do |
||
− | + | classes = function(this) |
|
− | + | local c = {} |
|
− | + | if this.c then |
|
+ | c = {"format-" .. tostring(this.c, 16)} |
||
− | out('<tr class="infoboxSectionHeader"><th class="infoboxSectionHeader" colspan="2">') |
||
− | end |
+ | end |
− | + | for k, v in pairs(this.fmt) do |
|
− | + | if v then |
|
+ | c[#c + 1] = "format-" .. k |
||
− | out('</th></tr>') |
||
− | |||
− | if title and (title ~= "") then |
||
− | count = count + 1 |
||
− | end |
||
− | |||
− | for j,subsection in ipairs(section) do |
||
− | if j > 1 then |
||
− | out('<tr class="infoboxSubsectionBreak"><td></td><td></td></tr>') |
||
− | end |
||
− | |||
− | for k, row in ipairs(subsection) do |
||
− | out('<tr>') |
||
− | for m,cell in ipairs(row) do |
||
− | out('<td') |
||
− | if #row == 1 then |
||
− | out(' colspan="2"') |
||
end |
end |
||
− | if type(cell)=="table" and cell.style then |
||
− | out(' style="'..cell.style..'"') |
||
− | end |
||
− | out('>') |
||
− | if type(cell)=="string" then |
||
− | out(wtprocessor(cell)) |
||
− | elseif type(cell)=="table" then |
||
− | out(wtprocessor(cell[1])) |
||
− | end |
||
− | count = count + 1 |
||
− | out('</td>') |
||
end |
end |
||
− | + | return table.concat(c, " ") |
|
+ | end, |
||
+ | text = function(this) |
||
+ | return table.concat(this.t, "") |
||
end |
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 |
end |
||
+ | return b |
||
end |
end |
||
+ | |||
− | |||
− | + | local escapes = {["#"] = ""} |
|
+ | for i, v in ipairs({"\\", "/", "&", "<", ">"}) do |
||
− | out('<tr><td colspan="2">' .. strings.errNoOutput .. '</td></tr>') |
||
+ | escapes[v] = v |
||
end |
end |
||
− | + | setmetatable( |
|
− | + | escapes, |
|
+ | {__index = function(tab, key) |
||
− | |||
− | return |
+ | return "\\" + key |
+ | end} |
||
− | end |
||
+ | ) |
||
− | |||
− | local function renderEmptyBox(frame, box) |
||
− | local presentedBox = calculateEffectiveInfobox{box=box.format, resolver = emptyBoxResolver} |
||
− | presentedBox.class="infoboxNoCollapse" |
||
− | |||
− | return renderBox(presentedBox, function(str) |
||
− | return frame:preprocess(str or "") |
||
− | end) |
||
− | end |
||
+ | local currblock = newblock() |
||
− | function p.calculateParameterListing(box) |
||
− | local |
+ | local blocks = {currblock} |
+ | local state = "text" -- text, escape, format |
||
− | for i,section in ipairs(box) do |
||
− | + | for w in text:gmatch(".") do |
|
− | + | if state == "escape" then |
|
+ | currblock(escapes[w]) |
||
− | if (not parameters[#parameters]) or (not (sectiondata.name == parameters[#parameters].name)) then |
||
− | + | state = "text" |
|
+ | elseif state == "text" then |
||
+ | if w == "\\" then |
||
+ | state = "escape" |
||
+ | elseif w == "&" then |
||
+ | state = "format" |
||
else |
else |
||
− | + | currblock(w) |
|
end |
end |
||
− | + | elseif state == "format" then |
|
− | if |
+ | 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) |
|
− | if subsection.desc then |
||
− | sectiondata[#sectiondata+1] = { text = subsection.desc } |
||
− | end |
||
− | for k,row in ipairs(subsection) do |
||
− | if not subsection.noDoc then |
||
− | if row.desc then |
||
− | sectiondata[#sectiondata+1] = { text = row.desc } |
||
− | end |
||
− | for m,cell in ipairs(row) do |
||
− | if (type(cell) == "table") and (cell.desc) and (not cell.noDoc) then |
||
− | sectiondata[#sectiondata+1] = cell |
||
− | end |
||
− | end |
||
− | end |
||
− | end |
||
− | end |
||
end |
end |
||
+ | state = "text" |
||
end |
end |
||
end |
end |
||
− | return parameters |
||
− | end |
||
+ | local outblocks = {} |
||
− | local function renderParameterListing(box) |
||
+ | for i, block in ipairs(blocks) do |
||
− | local parameters = p.calculateParameterListing(box) |
||
+ | local b = mw.html.create("span"):attr("class", block:classes()):wikitext(block:text()) |
||
− | |||
+ | outblocks[#outblocks + 1] = b |
||
− | local collapseByValue = function(tab) |
||
− | local iv={} |
||
− | for k,v in pairs(tab) do |
||
− | if not iv[v] then |
||
− | iv[v]={k} |
||
− | else |
||
− | iv[v][#(iv[v]) + 1] = k |
||
− | end |
||
− | end |
||
− | local s={} |
||
− | for k,v in pairs(iv) do |
||
− | if not s[v] then |
||
− | s[v]=k |
||
− | end |
||
− | end |
||
− | return s |
||
end |
end |
||
− | + | if args.nowrap then |
|
+ | local strings = {} |
||
− | for i,section in ipairs(parameters) do |
||
+ | for i, block in ipairs(outblocks) do |
||
− | if section.name then |
||
− | + | strings[#strings + 1] = tostring(block) |
|
end |
end |
||
+ | return table.concat(strings, "") |
||
− | for j,parameter in ipairs(section) do |
||
+ | else |
||
− | if parameter.text then |
||
+ | local out = mw.html.create("span"):addClass("craftingGridText") |
||
− | result = result .. parameter.text .. '\n' |
||
− | + | for i, block in ipairs(outblocks) do |
|
− | + | out:node(block) |
|
− | if type(parameter.arg) == "table" then |
||
− | namelist = table.concat(parameter.arg, "''' ''" .. strings.nameorname .. "'' '''") |
||
− | end |
||
− | result = result .. "* '''" .. namelist .. "''': " .. parameter.desc |
||
− | if parameter.type == "switch" then |
||
− | result = result .. " " .. strings.switchdoc .."\n" |
||
− | for key,value in util.orderedPairs(collapseByValue(parameter.allowedValues)) do |
||
− | result = result .. "** ''" .. table.concat(key, "''" .. strings.nameorname .. "''") .. "'': " .. value .. "\n" |
||
− | end |
||
− | elseif parameter.type == "image" then |
||
− | result = result .. " " .. strings.imagedoc .. "\n" |
||
− | else |
||
− | result = result .. "\n" |
||
− | end |
||
− | end |
||
end |
end |
||
+ | return tostring(out) |
||
end |
end |
||
− | return result |
||
end |
end |
||
− | + | function util.join(frame) |
|
+ | local out = {} |
||
− | return function(frame) |
||
− | + | for i, s in ipairs(frame.args) do |
|
+ | if not mw.ustring.match(s, "^%s*$") then |
||
− | local success, box = loadInfobox(frame.args[1], frame.args.forceUntranslated) |
||
− | + | out[#out + 1] = s |
|
− | out = "!!! " .. strings.loadFailure .. " [[Module:Infobox/" .. frame.args[1] .. util.pageSuffix() .. "]] " .. strings.informTranslationAdmin .. " \n\n" |
||
− | out = out .. box .. " !!!\n\n" |
||
− | else |
||
− | local success = nil |
||
− | local result = nil |
||
− | if frame.args.stackTrace then |
||
− | success, result = xpcall(function() |
||
− | func(frame, box) |
||
− | end, debug.traceback) |
||
− | else |
||
− | success, result = pcall(func, frame, box) |
||
− | end |
||
− | |||
− | if not success then |
||
− | out = "!!! " .. result .. " " .. strings.informTranslationAdmin .. " !!!\n\n" |
||
− | else |
||
− | out = result |
||
− | end |
||
− | end |
||
− | |||
− | if strings._ModuleStringTableProblem then |
||
− | out = strings._ModuleStringTableProblem .. "\n\n" .. out |
||
end |
end |
||
− | |||
− | return out |
||
end |
end |
||
+ | return table.concat(out, frame.args.sep) |
||
end |
end |
||
+ | -- This function is CASE-senstive. |
||
− | -- emit an infobox. |
||
+ | -- The title will be parsed and the first letter of the root page |
||
− | -- parameters: |
||
+ | -- will be capitalized automatically. |
||
− | -- 1: Infobox definition to use |
||
+ | function util.hasSubPage(f) |
||
− | -- everything else: whatever the infobox definition says |
||
+ | local args = f or {} |
||
− | |||
+ | if args == mw.getCurrentFrame() then |
||
− | p.infobox = makeInvokeWithBox(function(frame, box) |
||
+ | args = f.args or f:getParent().args |
||
− | local presentedBox = calculateEffectiveInfobox{box=box.format, resolver = makeResolver(frame)} |
||
− | |||
− | local out = renderBox(presentedBox, function(str) |
||
− | return frame:preprocess(str or "") |
||
− | end) |
||
− | |||
− | if (frame.args.class) or frame:getParent().args.class then |
||
− | out = out .. " [[Category:Articles that pass class to module infobox]] " |
||
end |
end |
||
+ | -- Trim whitespace |
||
− | |||
+ | args = require( [[Module:ProcessArgs]] ).norm(args) |
||
− | return out |
||
− | end) |
||
+ | -- If we are on a language page, account for that |
||
− | -- emit an infobox with the parameters displayed |
||
+ | local title = mw.title.new(args[1] or "") or {} |
||
− | -- parameters: |
||
+ | local subpage = args[2] |
||
− | -- 1: Infobox definition to use |
||
+ | local result = nil |
||
− | p.emptyBox = makeInvokeWithBox(function(frame, box) |
||
+ | if code(title) then |
||
− | return renderEmptyBox(frame, box) |
||
+ | local frame = mw.getCurrentFrame() |
||
− | end) |
||
+ | local args = {title.fullText, 1, -2} |
||
− | |||
+ | result = frame:callParserFunction("#titleparts", args) == subpage |
||
− | -- emit the parameter list for documentation |
||
− | -- parameters |
||
− | -- 1: Infobox definition to use |
||
− | p.parameterHelp = makeInvokeWithBox(function(frame, box) |
||
− | return renderParameterListing(box.format) |
||
− | end) |
||
− | |||
− | -- emit the entire documentation page |
||
− | -- parameters |
||
− | -- 1: infobox definition to use |
||
− | -- in the infobox definition |
||
− | -- intro is the text at the top |
||
− | -- postParameterText is displayed after the parameter listing |
||
− | -- examples is an array of examples: { { optional text = wikitext string, template-supporting wikitext}, ... } |
||
− | p.documentationPage = makeInvokeWithBox(function(frame, box) |
||
− | local fp = makePreprocess(frame) |
||
− | |||
− | local function hatnote(str) |
||
− | return frame:expandTemplate{title = "Hatnote", args = {str}} |
||
− | end |
||
− | |||
− | out = frame:expandTemplate{title = "Doc/Start"} |
||
− | |||
− | local noteText = interp(strings.generatedFrom, {template = "Module:Infobox/" .. frame.args[1]}) |
||
− | |||
− | out = out .. hatnote(fp(noteText)) .. '\n' |
||
− | out = out .. fp(strings.docLead) .. ' ' |
||
− | out = out .. fp(box.docLead) .. '\n\n' |
||
− | out = out .. "==" .. strings.parameters .. "==\n" |
||
− | out = out .. fp(strings.parameterLead) .. " " |
||
− | out = out .. fp(box.parameterLead) |
||
− | out = out .. renderEmptyBox(frame, box) .. '\n' |
||
− | out = out .. renderParameterListing(box.format) |
||
− | out = out .. '\n' .. fp(box.parameterTrail) |
||
− | |||
− | out = out .. '<div style="clear:both;"></div>' |
||
− | |||
− | out = out .. "\n==" .. strings.examples .. "==\n" |
||
− | if box.examples then |
||
− | for i, example in ipairs(box.examples) do |
||
− | out = out .. fp(example.text) .. '\n' |
||
− | out = out .. '<table class="wikitable">\n<tr><th>' .. strings.exampleCode .. '</th><th>' .. strings.exampleResult .. '</th></tr>\n' |
||
− | out = out .. '<tr>\n<td><pre>\n' .. example[1] .. '\n</pre></td>\n' |
||
− | out = out .. '<td>\n' .. fp(example[1]) .. '\n</td></tr>\n' |
||
− | out = out .. '</table>\n\n' |
||
− | end |
||
else |
else |
||
+ | result = title.subpageText == subpage |
||
− | out = out .. hatnote(fp(strings.noExamples)) |
||
end |
end |
||
+ | return result and 'true' or '' |
||
− | out = out .. frame:expandTemplate{title = "Doc/End"} |
||
+ | end |
||
− | return out |
||
− | end) |
||
− | return |
+ | return util |
Latest revision as of 05:19, 26 August 2020
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