Line 1: |
Line 1: |
| | | |
| local Utils = {} | | local Utils = {} |
| + | |
| + | -- * Function functions :V |
| + | |
| + | function Utils.id(...) |
| + | return ... |
| + | end |
| + | |
| + | function Utils.second(_, x) |
| + | return x |
| + | end |
| + | |
| + | -- * Number functions. |
| + | |
| + | function Utils.round(x) |
| + | return math.floor(x + 0.5) |
| + | end |
| + | |
| + | function Utils.divMod(x, y) |
| + | return math.floor(x / y), x % y |
| + | end |
| | | |
| -- * Collection functions. | | -- * Collection functions. |
| + | |
| + | function Utils.isize(xs) |
| + | local r = 0 |
| + | for _ in ipairs(xs) do |
| + | r = r + 1 |
| + | end |
| + | return r |
| + | end |
| + | |
| + | function Utils.size(xs) |
| + | local r = 0 |
| + | for _ in pairs(xs) do |
| + | r = r + 1 |
| + | end |
| + | return r |
| + | end |
| + | |
| + | function Utils.removekey(xs, k) |
| + | local e = xs[k] |
| + | xs[k] = nil |
| + | return e |
| + | end |
| + | |
| + | function Utils.isArray(xs) |
| + | for k, _ in pairs(xs) do |
| + | return k == 1 |
| + | end |
| + | return true |
| + | end |
| + | |
| + | function Utils.findBy(xs, p) |
| + | if Utils.isArray(xs) then |
| + | for k, v in ipairs(xs) do |
| + | if p(v, k) then |
| + | return v, k |
| + | end |
| + | end |
| + | else |
| + | for k, v in pairs(xs) do |
| + | if p(v, k) then |
| + | return v, k |
| + | end |
| + | end |
| + | end |
| + | return nil |
| + | end |
| | | |
| function Utils.find(tbl, v_, k_) | | function Utils.find(tbl, v_, k_) |
Line 8: |
Line 74: |
| 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 |
| end | | end |
Line 35: |
Line 110: |
| return k, v | | return k, v |
| end | | end |
| + | end |
| + | |
| + | function Utils.keys(tbl) |
| + | local result = {} |
| + | for k, _ in pairs(tbl) do |
| + | table.insert(result, k) |
| + | end |
| + | return result |
| + | end |
| + | |
| + | function Utils.values(tbl) |
| + | local result = {} |
| + | for _, v in pairs(tbl) do |
| + | table.insert(result, v) |
| + | end |
| + | return result |
| end | | end |
| | | |
Line 53: |
Line 144: |
| end | | end |
| return false | | return false |
| + | end |
| + | |
| + | function Utils.imax(arr, def) |
| + | 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 or def |
| + | end |
| + | |
| + | function Utils.imin(arr, def) |
| + | local minValue |
| + | local minIndex |
| + | for i, v in ipairs(arr) do |
| + | if not minValue or v < minValue then |
| + | minValue = v |
| + | minIndex = i |
| + | end |
| + | end |
| + | return minValue or def |
| end | | end |
| | | |
Line 77: |
Line 192: |
| return k, v | | return k, v |
| end | | end |
| + | end |
| + | |
| + | function Utils.ilast(arr) |
| + | return arr[#arr] |
| end | | end |
| | | |
Line 90: |
Line 209: |
| end | | end |
| return arr1 | | return arr1 |
| + | end |
| + | |
| + | Utils.join = table.concat |
| + | |
| + | function Utils.ijoin(arr, sep) |
| + | sep = sep or "" |
| + | return table.concat(arr, sep) |
| + | end |
| + | |
| + | function Utils.joinLines(arr) |
| + | return table.concat(arr, "\n") |
| end | | end |
| | | |
Line 96: |
Line 226: |
| end | | end |
| | | |
− | -- * String functions. | + | function Utils.sort(tbl, f) |
| + | table.sort(tbl, f) |
| + | return tbl |
| + | end |
| + | |
| + | function Utils.isort(arr, f) |
| + | local result = Utils.icopy(arr) |
| + | table.sort(result, f) |
| + | 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.category(name) | + | function Utils.round(x) |
− | return "[[" .. "Category:" .. name .. "]]" | + | return x % 2 ~= 0.5 and math.floor(x + 0.5) or x - 0.5 |
| end | | end |
| + | |
| + | -- * String functions. |
| | | |
| function Utils.pad(s, n, c) | | function Utils.pad(s, n, c) |
Line 122: |
Line 273: |
| s = s:gsub("^(%l)", function(a) return string.upper(a) end) | | s = s:gsub("^(%l)", function(a) return string.upper(a) end) |
| return s | | 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 | | end |
| | | |
Line 183: |
Line 344: |
| local success, data = pcall(function () return require(string.format("Module:%s", name)) end) | | 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 | | -- 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 |
| + | |
| + | function Utils.loadData(name) |
| + | local success, data = pcall(function () return mw.loadData(string.format("Module:%s", name)) end) |
| + | -- TODO: ??? |
| if data == true then | | if data == true then |
| return false, nil | | return false, nil |
Line 201: |
Line 372: |
| ) | | ) |
| if type(v) == "table" then | | if type(v) == "table" then |
− | debugPrint(v, i + 1) | + | Utils.debugPrint(v, i + 1) |
| end | | end |
| end | | end |
Line 207: |
Line 378: |
| mw.log(tostring(x) .. " : " .. type(x)) | | mw.log(tostring(x) .. " : " .. type(x)) |
| end | | 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 | | end |
| | | |
Line 225: |
Line 419: |
| end | | end |
| 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 |
| + | |
| + | Utils.romanizable = {'zh-hans', 'zh-hant', 'zh-cn', 'zh-tw', 'zh-hk', 'zh-sg', 'zh-mo', 'zh-my', 'ja', 'ko', 'vi'} |
| + | |
| + | Utils.normalization_table = {[' '] = ' ', ['~'] = '~', ['!'] = '!', ['?'] = '?'} |
| + | |
| + | -- checks if string is set and if it's non-empty |
| + | function Utils.isset(target) |
| + | return target ~= nil and target ~= "" |
| + | end |
| + | |
| + | --[[ simulates the (a ? b : c) notation of C/C++ and PHP languages. |
| + | Similar to {{#if:{{{a|}}}|{{{b|}}}|{{{c|}}}}} from parser functions. ]] |
| + | function Utils.cv(a, b, c) |
| + | if a then return b else return c end |
| + | end |
| + | |
| + | --slices a table to return another table containing values within a certain range |
| + | --source: http://snippets.luacode.org/snippets/Table_Slice_116 |
| + | function Utils.sliceTable (values,i1,i2) |
| + | local res = {} |
| + | local n = #values |
| + | -- default values for range |
| + | i1 = i1 or 1 |
| + | i2 = i2 or n |
| + | if i2 < 0 then |
| + | i2 = n + i2 + 1 |
| + | elseif i2 > n then |
| + | i2 = n |
| + | end |
| + | if i1 < 1 or i1 > n then |
| + | return {} |
| + | end |
| + | local k = 1 |
| + | for i = i1,i2 do |
| + | res[k] = values[i] |
| + | k = k + 1 |
| + | end |
| + | return res |
| + | end |
| + | |
| + | -- checks if a given page exists |
| + | function Utils.exists(page) |
| + | if not Utils.isset(page) then return false end |
| + | return mw.getCurrentFrame():preprocess('{{#ifexist:' .. page .. '|1|0}}') == '1' |
| + | end |
| + | |
| + | --[[ Tries to get contents of a page with given name. |
| + | If the function fails it returns nil (page doesn't exist or can't be loaded) |
| + | On success it returns the contents of page (it can be be partially preprocessed, so watch out for parser markers). ]] |
| + | function Utils.getPage(name) |
| + | if not Utils.isset(name) then return nil end |
| + | |
| + | local frame = mw.getCurrentFrame() |
| + | |
| + | -- do a safe call to catch possible errors, like "template doesn't exist" |
| + | local stat,page = pcall(frame.expandTemplate, frame, {title = ':' .. name}) |
| + | |
| + | if not stat then |
| + | -- TODO: 'page' contains the error message. Do some debugging? |
| + | return nil |
| + | end |
| + | |
| + | return page |
| + | end |
| + | |
| + | function Utils.stripTags(text) |
| + | if Utils.isset(text) then |
| + | local tmp |
| + | repeat |
| + | tmp = text |
| + | -- a pair of tags, like <td style="">...</td> |
| + | text = string.gsub(text, '<%s*(%w+).->(.-)<%s*/%s*%1%s*>', '%2') |
| + | -- closed tag, like <br/> |
| + | text = string.gsub(text, '<%s*%w+%s*/%s*>', '') |
| + | until tmp == text |
| + | end |
| + | return text |
| + | end |
| + | |
| + | --[[ Sort table and remove repeating elements. |
| + | Since tbl is passed as reference, any changes in it will affect the passed table. ]] |
| + | function Utils.trunkTable(tbl) |
| + | table.sort(tbl) |
| + | local last |
| + | local redo |
| + | repeat |
| + | redo = false |
| + | last = nil |
| + | for k,v in pairs(tbl) do |
| + | if v ~= last then |
| + | last = v |
| + | else |
| + | table.remove(tbl, k) |
| + | redo = true |
| + | break |
| + | end |
| + | end |
| + | until not redo |
| + | end |
| + | |
| + | --[[ Checks if a given value is among the elements of a table and returns its index. |
| + | Returns nil if it can't find it. ]] |
| + | function Utils.isInTable(tbl, val) |
| + | for k,v in pairs(tbl) do |
| + | if v == val then return k end |
| + | end |
| + | return nil |
| + | end |
| + | |
| + | --[[ Compare 'n' elements in two tables, starting from 's1' in first table and 's2' in second table. ]] |
| + | function Utils.partialTableCompare(t1, t2, s1, s2, n) |
| + | if n < 1 then return true end -- basically there's nothing to compare, so no differences were found |
| + | |
| + | for i = 0,(n-1) do |
| + | -- Note that nil values are also valid. |
| + | if t1[s1+i] ~= t2[s2+i] then return false end |
| + | end |
| + | |
| + | return true |
| + | end |
| + | |
| + | -- naming based on "Navbox" elements |
| + | Utils.main_colors = { |
| + | game = {border = '#A88580', title = '#FFC9C2', above = '#FFD1CA', group = '#FFD9D2', subgroup = '#FFE1DA', dark = '#FFEEE8', background = '#FFF4EE'}; |
| + | music = {border = '#A8A077', title = '#FFF3B4', above = '#FFF6C0', group = '#FFF7C8', subgroup = '#FFF8D0', dark = '#FFFBE4', background = '#FFFBEE'}; |
| + | printwork = {border = '#9298A8', title = '#DDE6FF', above = '#E1E7FF', group = '#E6E9FF', subgroup = '#EAECFF', dark = '#EDF2FF', background = '#F4F9FF'}; |
| + | } |
| + | |
| + | -- function for easy adding of styles to html tags |
| + | function Utils.addStyle(tbl, val) |
| + | if Utils.isset(val) then |
| + | local av = tostring(val):gsub("^%s*(.-)%s*$", "%1") |
| + | if string.sub(av, -1) ~= ';' then av = av .. ';' end |
| + | tbl[#tbl+1] = av |
| + | end |
| end | | end |
| | | |
| return Utils | | return Utils |