Line 1: |
Line 1: |
− | local Module = {}
| |
| | | |
− | -- * Function functions.
| + | local Utils = {} |
| | | |
− | function Module.id(x) | + | -- * Function functions :V |
− | return x
| + | |
| + | function Utils.id(x) |
| + | return x |
| + | end |
| + | |
| + | -- * Number functions. |
| + | |
| + | function Utils.round(x) |
| + | return math.floor(x + 0.5) |
| end | | end |
| | | |
| -- * Collection functions. | | -- * Collection functions. |
| | | |
− | function Module.find(tbl, v_, k_) | + | function Utils.find(tbl, v_, k_) |
− | for _, v in pairs(tbl) do
| + | for _, v in pairs(tbl) do |
− | if k_ and v and v[k_] == v_ or not k_ and v == v_ then
| + | if k_ and v and v[k_] == v_ or not k_ and v == v_ then |
− | return v
| + | return v |
| + | end |
| + | end |
| + | return false |
| + | end |
| + | |
| + | function Utils.includes(tbl, v_, k_) |
| + | for _, v in pairs(tbl) do |
| + | if k_ and v and v[k_] == v_ or not k_ and v == v_ then |
| + | return true |
| + | end |
| + | end |
| + | return false |
| + | end |
| + | |
| + | function Utils.map(tbl, fn) |
| + | local result = {} |
| + | for k, v in pairs(tbl) do |
| + | table.insert(result, fn(v, k)) |
| + | end |
| + | return result |
| + | end |
| + | |
| + | function Utils.filter(tbl, pred) |
| + | local result = {} |
| + | for k, v in pairs(tbl) do |
| + | if pred(v, k) then |
| + | table.insert(result, v) |
| + | end |
| + | end |
| + | return result |
| + | end |
| + | |
| + | function Utils.first(tbl) |
| + | for k, v in pairs(tbl) do |
| + | return k, v |
| + | end |
| + | end |
| + | |
| + | function Utils.keys(tbl) |
| + | local result = {} |
| + | for k, _ in pairs(tbl) do |
| + | table.insert(result, k) |
| + | end |
| + | return result |
| + | end |
| + | |
| + | function Utils.ifind(arr, v_) |
| + | for i, v in ipairs(arr) do |
| + | if v == v_ then |
| + | return i |
| + | end |
| + | end |
| + | return false |
| + | end |
| + | |
| + | function Utils.ifindBy(arr, p) |
| + | for i, v in ipairs(arr) do |
| + | if p(v) then |
| + | return v, i |
| + | end |
| + | end |
| + | return false |
| + | end |
| + | |
| + | function Utils.imax(arr) |
| + | local maxValue |
| + | local maxIndex |
| + | for i, v in ipairs(arr) do |
| + | if not maxValue or v > maxValue then |
| + | maxValue = v |
| + | maxIndex = i |
| + | end |
| + | end |
| + | return maxValue |
| + | end |
| + | |
| + | function Utils.imap(arr, fn) |
| + | local result = {} |
| + | for i, v in ipairs(arr) do |
| + | table.insert(result, fn(v, i)) |
| + | end |
| + | return result |
| + | end |
| + | |
| + | function Utils.ifilter(arr, pred) |
| + | local result = {} |
| + | for i, v in ipairs(arr) do |
| + | if pred(v, i) then |
| + | table.insert(result, v) |
| + | end |
| + | end |
| + | return result |
| + | end |
| + | |
| + | function Utils.ifirst(arr) |
| + | for k, v in ipairs(arr) do |
| + | return k, v |
| + | end |
| + | end |
| + | |
| + | function Utils.insertNew(arr, el) |
| + | if not Utils.find(arr, el) then |
| + | table.insert(arr, el) |
| + | end |
| + | end |
| + | |
| + | function Utils.concat(arr1, arr2) |
| + | for i = 1, #arr2 do |
| + | arr1[#arr1 + 1] = arr2[i] |
| + | end |
| + | return arr1 |
| + | end |
| + | |
| + | Utils.join = table.concat |
| + | |
| + | function Utils.ijoin(arr, sep) |
| + | sep = sep or "" |
| + | return table.concat(arr, sep) |
| + | end |
| + | |
| + | function Utils.icopy(arr) |
| + | return Utils.imap(arr, function(v) return v end) |
| + | end |
| + | |
| + | function Utils.isort(arr) |
| + | local result = Utils.icopy(arr) |
| + | table.sort(result) |
| + | return result |
| + | end |
| + | |
| + | function Utils.isum(arr, result) |
| + | result = result or 0 |
| + | for _, v in ipairs(arr) do |
| + | result = result + (v or 0) |
| + | end |
| + | return result |
| + | end |
| + | |
| + | -- * Number functions. |
| + | |
| + | function Utils.round(x) |
| + | return x % 2 ~= 0.5 and math.floor(x + 0.5) or x - 0.5 |
| + | end |
| + | |
| + | -- * String functions. |
| + | |
| + | function Utils.pad(s, n, c) |
| + | c = c or " " |
| + | n = n or 0 |
| + | s = tostring(s) or "" |
| + | return #s < n and string.rep(c, n - #s) .. s or s |
| + | end |
| + | |
| + | function Utils.trim(s) |
| + | return string.gsub(s, "^%s*(.-)%s*$", "%1") |
| + | end |
| + | |
| + | function Utils.startsWith(s, ss) |
| + | return string.sub(s, 1, string.len(ss)) == ss |
| + | end |
| + | |
| + | -- Capitalize each word in a string. |
| + | function Utils.capitalize(s) |
| + | s = s:gsub("(%s)(%l)", function(a, b) return a .. string.upper(b) end) |
| + | s = s:gsub("^(%l)", function(a) return string.upper(a) end) |
| + | return s |
| + | end |
| + | |
| + | -- * Wikitext/HTML functions. |
| + | |
| + | function Utils.category(name) |
| + | return "[[" .. "Category:" .. name .. "]]" |
| + | end |
| + | |
| + | function Utils.red(s) |
| + | return Utils.format{[[<span style="color:red">${s}</span>]], s = s} |
| + | end |
| + | |
| + | -- * Calling arbitrary Lua functions using #invoke. |
| + | |
| + | -- Used to call Formatting:tooltip in Template:Tooltip, mainly because Lua code properly escapes characters, |
| + | -- so that span's title attribute always works. |
| + | function Utils.method(frame) |
| + | local m = require("Module:" .. frame.args[1]) |
| + | local f = frame.args[2] |
| + | local args = {} |
| + | for k, v in ipairs(frame.args) do |
| + | if type(k) == "number" and k >= 3 and type(v) == "string" then |
| + | table.insert(args, v) |
| + | end |
| + | end |
| + | return m[f](m, unpack(args)) |
| + | end |
| + | |
| + | -- * Frame functions. |
| + | |
| + | local getArgs = require("Module:GetArgs") |
| + | |
| + | -- Unused. |
| + | function Utils.getContext(frame) |
| + | local frame1 = frame:getParent() |
| + | if frame1 then |
| + | local frame2 = frame1:getParent() |
| + | if frame2 then |
| + | return { pagename = frame2:getTitle(), args = getArgs{ frame = frame2 } } |
| + | else |
| + | return { pagename = frame1:getTitle(), args = getArgs{ frame = frame1 } } |
| + | end |
| + | else |
| + | return { pagename = frame:getTitle(), args = getArgs{ frame = frame } } |
| end | | end |
− | end
| |
− | return nil
| |
| end | | end |
| | | |
− | function Module.findBy(tbl, fn) | + | -- getParent -> getArgs |
− | for k, v in pairs(tbl) do
| + | function Utils.getParentArgs(frame) |
− | if fn(v, k) then | + | local frame1 = frame:getParent() |
− | return v, k
| + | if frame1 then |
| + | return getArgs{ frame = frame1 } |
| + | else |
| + | return nil |
| end | | end |
− | end
| |
− | return nil
| |
| end | | end |
| | | |
− | return Module | + | -- getArgs + getParent -> getArgs, "implicit" args can be defined in the template (e.g. pagename={{PAGENAME}}) |
| + | -- "explicit" args are user defined. |
| + | function Utils.getTemplateArgs(frame) |
| + | local frame1 = frame:getParent() |
| + | if frame1 then |
| + | return { implicit = getArgs{ frame = frame }, explicit = getArgs{ frame = frame1 } } |
| + | else |
| + | return { implicit = getArgs{ frame = frame }, explicit = {} } |
| + | end |
| + | end |
| + | |
| + | function Utils.requireModule(name) |
| + | local success, data = pcall(function () return require(string.format("Module:%s", name)) end) |
| + | -- module without return (or empty, nil, false, true return) gives success = true, data = true |
| + | if data == true then |
| + | return false, nil |
| + | else |
| + | return success, data |
| + | end |
| + | end |
| + | |
| + | -- * Testing functions. |
| + | |
| + | function Utils.debugPrint(x, i) |
| + | i = i or 0 |
| + | if type(x) == "table" then |
| + | for k, v in pairs(x) do |
| + | mw.log( |
| + | string.rep(" ", i) .. tostring(k) .. " : " .. type(k) .. " = " .. |
| + | (type(v) == "table" and "table" or tostring(v) .. " : " .. type(v)) |
| + | ) |
| + | if type(v) == "table" then |
| + | Utils.debugPrint(v, i + 1) |
| + | end |
| + | end |
| + | else |
| + | mw.log(tostring(x) .. " : " .. type(x)) |
| + | end |
| + | end |
| + | |
| + | local function showValue(v) |
| + | return type(v) == "string" and string.format('"%s"', v) or type(v) == "function" and '"function"' or tostring(v) |
| + | end |
| + | |
| + | function Utils.js(x, i) |
| + | i = i or 0 |
| + | local r = "" |
| + | if type(x) == "table" then |
| + | r = "{\n" |
| + | for k, v in pairs(x) do |
| + | if type(v) == "table" then |
| + | r = r .. string.rep(" ", i + 1) .. tostring(k) .. ": " .. Utils.js(v, i + 1) |
| + | else |
| + | r = r .. string.rep(" ", i + 1) .. tostring(k) .. ": " .. showValue(v) .. ",\n" |
| + | end |
| + | end |
| + | r = r .. string.rep(" ", i) .. (i == 0 and "}\n" or "},\n") |
| + | else |
| + | return showValue(x) |
| + | end |
| + | return r |
| + | end |
| + | |
| + | function Utils.registerFormatTests(obj, tests, fn) |
| + | obj.run_format_tests = function() |
| + | for _, test in ipairs(tests) do |
| + | local result = obj.format(nil, test) |
| + | mw.log(fn and fn(result) or result) |
| + | end |
| + | end |
| + | end |
| + | |
| + | function Utils.registerTableTests(obj, tests, fn) |
| + | obj.run_table_tests = function() |
| + | for _, test in ipairs(tests) do |
| + | local result = obj:Table(test) |
| + | mw.log(fn and fn(result) or result) |
| + | end |
| + | end |
| + | end |
| + | |
| + | function Utils.test(obj, desc, fn) |
| + | obj.test = function() |
| + | mw.log(desc) |
| + | fn(obj) |
| + | mw.log("ok") |
| + | end |
| + | end |
| + | |
| + | Utils.log = mw.log |
| + | |
| + | function Utils.format(s, lookup) |
| + | if not lookup then |
| + | lookup = s |
| + | s = lookup[1] |
| + | table.remove(lookup, 1) |
| + | end |
| + | return (string.gsub(s, '${([^%.%!%:%}%[%]]+)%[?([^%.%!%:%}%]]*)%]?%.?([^%!%:%}]*)!?([^%:%}]?):?([^%}]*)}', |
| + | function(name, element, attribute, conversion, format_spec) |
| + | --local start_of_value, end_of_value, value = string.find(x, '^${(.-)[[.!:}]') |
| + | --local start_of_access, end_of_access, access = string.find(x, '^${.-%[(.-)%][!:}]') |
| + | --if not access then |
| + | -- start_of_access, end_of_access, access = string.find(x, '^${[^:]-%.(.-)[!:}]') |
| + | --end |
| + | --local start_of_conversion, end_of_conversion, conversion = string.find(x, '^${.-!(.)[:}]') |
| + | --local start_of_format_spec, end_of_format_spec, format_spec = string.find(x, ':(.*)}$') |
| + | |
| + | local value = lookup[name] |
| + | if string.len(element) > 0 then |
| + | value = value[element] |
| + | elseif string.len(attribute) > 0 then |
| + | value = value[attribute] |
| + | end |
| + | |
| + | if string.len(conversion) > 0 then |
| + | if conversion == 's' then |
| + | value = tostring(value) |
| + | end |
| + | end |
| + | |
| + | if string.len(format_spec) > 0 then |
| + | local start_of_sign, end_of_sign, sign = string.find(format_spec, '([+%- ])') |
| + | local start_of_width, end_of_width, width, comma, precision, option = string.find(format_spec, '(%d*)(,?)([.0-9]*)([bcdeEfFgGnosxX%%]?)$') |
| + | precision = string.sub(precision, 2) |
| + | local number = tonumber(value) |
| + | if #width > 0 then |
| + | if number then |
| + | value = string.format(string.format(number % 1 ~= 0 and "%%0%s.%sf" or "%%0%sd", width, #precision > 0 and precision or 0), number) |
| + | end |
| + | value = string.format(string.format("%%0%ss", width), value) |
| + | elseif #precision > 0 and number then |
| + | value = string.format(string.format("%%0%s.%sf", width, precision), number) |
| + | end |
| + | if sign then |
| + | if number and number > 0 then |
| + | if sign == "+" then |
| + | value = "+" .. value |
| + | elseif sign == " " then |
| + | value = " " .. value |
| + | end |
| + | end |
| + | end |
| + | end |
| + | |
| + | return value |
| + | end)) |
| + | end |
| + | |
| + | function Utils.split(string, separator, max_split, plain) |
| + | assert(separator ~= '') |
| + | assert(max_split == nil or max_split >= 1) |
| + | |
| + | local default_separator = false |
| + | |
| + | local result = {} |
| + | |
| + | if not separator or separator == '' then |
| + | separator = '%s+' |
| + | plain = false |
| + | string = mw.text.trim(string) |
| + | if string == '' then |
| + | return result |
| + | end |
| + | end |
| + | |
| + | max_split = max_split or -1 |
| + | |
| + | local item_index, start_index = 1, 1 |
| + | local separator_start, separator_end = mw.ustring.find(string, separator, start_index, plain) |
| + | while separator_start and max_split ~= 0 do |
| + | result[item_index] = mw.ustring.sub(string, start_index, separator_start - 1) |
| + | item_index = item_index + 1 |
| + | start_index = separator_end + 1 |
| + | separator_start, separator_end = mw.ustring.find(string, separator, start_index, plain) |
| + | max_split = max_split - 1 |
| + | end |
| + | result[item_index] = mw.ustring.sub(string, start_index) |
| + | |
| + | return result |
| + | end |
| + | |
| + | return Utils |