FANDOM


--[[Category:Lua modules]]
--<nowiki>
-- Contains various utlity functions.
-- A suggestable name/usage is utils = requires("Module:Utils")
 
local utils = {}
 
--[[ Checks if the passed value represents "true".
Returns true if val is anything other than
    0, "0", false, "false", nil, or "nil".
@param val: Anything.
@returns Boolean. True if val semantically means "true". False otherwise.
--]]
function utils.tobool(val)
    val = tostring(val)
    return val ~= '0' and val ~= 'false' and val ~= 'nil'
end
 
-- String interpolation
-- Usage: print( interp("${name} is ${value}", {name = "foo", value = "bar"}) )
-- Source: http://lua-users.org/wiki/StringInterpolation
function utils.format(s, tab)
    return (s:gsub('($%b{})', function(w) return tab[w:sub(3, -2)] or w end))
end
 
-- String interpolation that shows the variable name in when hovered
-- Intended for debugging.
function utils.debug_format(s, tab)
    return (s:gsub('($%b{})', function(w)
        return tab[w:sub(3, -2)] and ('<span title="' .. w:sub(3, -2) .. '" style="text-decoration:underline dotted;">' .. tab[w:sub(3, -2)] .. '</span>')
            or w end))
end
 
--[[ Adds a comma to numbers at 1,000 or above.
Not intended to be used with numbers 1,000,000 and higher.
Ex: add_thousands_comma('9') => '9'
Ex: add_thousands_comma(9000) => '9,000'
Ex: add_thousands_comma(1234) => '1,234'
Ex: add_thousands_comma(123456789) => '123456,789' (Not intended for this)
Ex: add_thousands_comma('g') => nil (Wrong usage)
@param num String or Int. The number to work with.
@returns String or nil. The comma-added number on success, or nil on failure.
--]]
function utils.add_thousands_comma(num)
    num = tonumber(num)
    if not num then
        return nil
    end
    over_1k_part, under_1k_part = math.modf(num / 1000.0)
    -- Avoid roundoff errors.
    under_1k_part = math.floor(under_1k_part * 1000 + 0.5)
    if over_1k_part <= 0 then
        return tostring(under_1k_part)
    end
    return string.format('%d,%03d', over_1k_part, under_1k_part)
end
 
--[[ Gives the visible length of a Unicode string.
It MIGHT give incorrect results.
The recipe comes from this link:
http://lua-users.org/wiki/LuaUnicode
--]]
function utils.ustrlen(word)
    _, count = string.gsub(word, "[^\128-\193]", "")
    return count
end
 
utils.macroned_vowel_to_lower = {['Ā'] = 'ā', ['Ē'] = 'ē', ['Ī'] = 'ī', ['Ō'] = 'ō', ['Ū'] = 'ū',}
-- Lowercases the first letter of a string.
function utils.lowercase(word)
    macroned_first_char = utils.macroned_vowel_to_lower[string.sub(word, 1, 2)]
    if macroned_first_char then
        -- The first letter was a TWO byte macroned vowel.
        -- The rest of the string is from byte THREE and onwards.
        return macroned_first_char .. string.sub(word, 3)
    end
    -- The first letter was something other than a macroned vowel.
    -- Let Lua try to uppercase the first character.
    -- The rest of the string is from byte TWO and onwards.
    return string.lower(string.sub(word, 1, 1)) .. string.sub(word, 2)
end
 
utils.macroned_vowel_to_upper = {['ā'] = 'Ā', ['ē'] = 'Ē', ['ī'] = 'Ī', ['ō'] = 'Ō', ['ū'] = 'Ū',}
-- Uppercases the first letter of a string.
function utils.uppercase(word)
    macroned_first_char = utils.macroned_vowel_to_upper[string.sub(word, 1, 2)]
    if macroned_first_char then
        -- The first letter was a TWO byte macroned vowel.
        -- The rest of the string is from byte THREE and onwards.
        return macroned_first_char .. string.sub(word, 3)
    end
    -- The first letter was something other than a macroned vowel.
    -- Let Lua try to uppercase the first character.
    -- The rest of the string is from byte TWO and onwards.
    return string.upper(string.sub(word, 1, 1)) .. string.sub(word, 2)
end
 
-- Makes the outputted text emphasized (red and bold).
function utils.emphasize(str)
    return "<span style=\"color:red; font-weight: bold\">" .. str .. "</span>"
end
 
-- Makes a string "safe" to be output to the Wikia.
-- It removes links and HTML tags.
-- Styling should be left to the module that calls this function.
function utils.sanitizeString(str)
    -- Remove links like [blah] and [[blah]]
    str = (string.gsub(str, "[", ""))
    str = (string.gsub(str, "]", ""))
    -- Remove embedded styling like <div>Pointless spacing</div>
    str = (string.gsub(str, "<", ""))
    str = (string.gsub(str, "/>", ""))
    return str
end
 
-- The following three functions sort a list.
-- Give a table to orderedPairs to use it.
-- The recipe is from http://lua-users.org/wiki/SortedIteration
function __genOrderedIndex( t )
    local orderedIndex = {}
    for key in pairs(t) do
        table.insert( orderedIndex, key )
    end
    table.sort( orderedIndex )
    return orderedIndex
end
 
function utils.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.
 
    local key = nil
    --print("orderedNext: state = "..tostring(state) )
    if state == nil then
        -- the first time, generate the index
        t.__orderedIndex = __genOrderedIndex( t )
        key = t.__orderedIndex[1]
    else
        -- fetch the next value
        for i = 1,table.getn(t.__orderedIndex) do
            if t.__orderedIndex[i] == state then
                key = t.__orderedIndex[i+1]
            end
        end
    end
 
    if key then
        return key, t[key]
    end
 
    -- no more value to return, cleanup
    t.__orderedIndex = nil
    return
end
 
function utils.orderedPairs(t)
    -- Equivalent of the pairs() function on tables. Allows to iterate
    -- in order
    return utils.orderedNext, t, nil
end
 
-- Turns named elements of a table into keys set to "true".
-- This is useful for reformating the table to check variables for assignment.
-- Example: {fat='cat', 'dont_feed'} becomes {fat='cat', dont_feed='true'}
function utils.getTruthCheckableTable(table)
    local newTable = {}
    for key, value in pairs(table) do
        if type(key) == 'number' then
            -- This is a positional argument.
            -- eg. Turn 'dont_feed' into dont_feed=true
            newTable[value] = true
        else
            -- This is a named argument.
            -- Just copy the key-value pair into the new table.
            newTable[key] = value
        end
    end
    return newTable
end
 
-- Gets the contents of a table as a readable list.
-- This is a very simple function. It won't tell the contents of nested tables.
function utils.getTableAsList(tab)
    local s = ''
    for key, val in pairs(tab) do
        if type(val) ~= string then
            val = tostring(val)
        end
        s = s .. '\n*[' .. key .. '] = ' .. val
    end
    return s
end
 
-- Makes all table elements accessible from the topmost level
-- For example: tableX["names"]["english"] becomes tableX["names_english"]
--
-- This function is useful for the string interpolation function in
-- Module:UtilityFunctions because the string interpolation will not
-- handle nested tables.
--
-- This function is recursive.
--
-- @param tab: Table. The table to flatten.
-- @returns: The flattened table.
function utils.flattenTable(tab)
	local flat_tab = {}
	for key, val in pairs(tab) do
		if type(val) ~= 'table' then
			flat_tab[key] = val
		else
			-- This key-val pair points to another table.
			-- Flatten this table so that we can stringify all nested data.
			local nested_table = utils.flattenTable(val)
			for k, v in pairs(nested_table) do
				-- Don't keep the tables within the tables.
				if type(v) ~= 'table' then
					flat_tab[key .. '_' .. k] = v
				end
			end
		end
	end
	return flat_tab
end
 
-- Merges two table into one with the second table's value appended after the first
-- For example: {'1a','2a'} and {'1b','2b'} becomes {'1a','1b','2a','2b'} 
-- @table1: First table. Will be the first part of the merged table.
-- @table2: Second Table. Will be the second part of the merged table.
-- @returns: The merged table.
function utils.mergeTable(table1,table2)
    for k,v in pairs(table2) do
        table.insert(table1, v)
    end
    return table1
end
 
-- Merges two table values in alternating values
-- For example: {'1a','2a'} and {'1b','2b'} becomes {'1a','1b','2a','2b'} 
--
-- @table1: First table. Will be the first value in table in alternating order.
-- @table2: Second Table. Will be the last value in table in alternating order.
-- @returns: The merged table.
function utils.alternatingMerge(table1,table2)
    local resultTable = {}
 
    local indexLength = table.getn(table1) > table.getn(table2) and table.getn(table1) 
    or table.getn(table2)
 
    for key = 1, indexLength, 1 do
        table.insert(resultTable,table1[key])
        table.insert(resultTable,table2[key])
    end
 
    return resultTable
end
 
--[[ Gets a Lua table as a JSON list.
Example:
=p.tableToJSON({foo='cat', bar='dog', foobar='cow'})
returns '{"foo":"cat","bar":"dog","foobar":"cow"}'
which you can use from JavaScript using JSON.parse(theReturnValue) .
The order may not be preserved.
 
@arg    tbl A Lua table.
@return String. A JSON table.
--]]
function utils.tableToJSON(tbl)
    local JSONStringTable = {}
    local entry
    for k, v in pairs(tbl) do
        entry = string.format('"%s":"%s"', k, v)
        table.insert(JSONStringTable, entry)
    end
    JSONStringTable = '{' .. table.concat(JSONStringTable, ',') .. '}'
    return JSONStringTable
end
 
--[[ Merges two Lua tables with the exact same keys into a JSON list.
The intention is that the keys of both tables can be used internally
amongst other Lua scripts (such as evolution tiers being 1 to 4), but
for JSON, we might represent the key as something different (like "e"),
and the value as something different as well (like "evo").
 
This crazy methodology was conceived to relate abbreviations of CSS classes
to longer, unique CSS class names. Since the classes would be referenced many
times, I thought that this would be the most scalable solution. Instead of Lua
expanding the long CSS classes from wikitext into HTML, Lua would output the
this table of abbreviations, and JavaScript would replace the abbreviations
with the full CSS names.
 
Example:
=p.mergeTablesToJSON(
    {[1]='c',   [2]='d',   ['somekey']='e'},
    {[1]='CAT', [2]='DOG', ['somekey']='ELF'}
    )
returns '{"c":"CAT","d":"DOG","e":"ELF"}'
which you can use from JavaScript using JSON.parse(theReturnValue) .
The order may not be preserved.
 
@arg    keysTbl A Lua table. The values in this will become the JSON keys.
@arg    valsTbl A Lua table. The values in this will become the JSON values.
@return String. A JSON table.
--]]
function utils.mergeTablesToJSON(keysTbl, valsTbl)
    local JSONStringTable = {}
    local entry
    for tableKey, JSONKey in pairs(keysTbl) do
        entry = string.format('"%s":"%s"', JSONKey, valsTbl[tableKey])
        table.insert(JSONStringTable, entry)
    end
    JSONStringTable = '{' .. table.concat(JSONStringTable, ',') .. '}'
    return JSONStringTable
end
 
--[[ Copies a table. Good for dealing with data from mw.loadData.
--]]
function utils.simpleCopy(tbl)
    local tbl2 = {}
    for k, v in pairs(tbl) do
        tbl2[k] = v
    end
    return tbl2
end
 
--[=[ @description Makes an error message for output to a Wikia page.
It's output is like
[[Module:MyMod|Error]]: Something bad happened[[Category:Errors]]
Example debugger usage:
=p.getErrorMsg('This is a test error.')
=p.getErrorMsg('Something happened.', 'SomeModule')
=p.getErrorMsg('Bad stuff.', 'BrokenModule', 'Pages with issues')
=p.getErrorMsg('Please help.', 'BustedModule', '[[Category:Pages with bugs]]')
@param msg String. The error message. Required.
@param sourceModule String. The module the message came from. Optional.
    Defaults to an empty string.
@param category String. The category to append to the the message. Optional.
    Defaults to the category for bugs.
@returns String. The full error message for the Wikia Page.
--]=]
function utils.getErrorMsg(msg, sourceModule, category)
    -- Make the category parameter into a real category
    if not category then
        category = mw.loadData('Module:Categories').general.bug
    elseif type(category) == 'string' then
        if string.sub(category, 1, 11) ~= '[[Category:' then
            category = '[[Category:' .. category
        end
 
        local idx = #category
        if string.sub(category, idx - 2, idx) ~= ']]' then
            category = category .. ']]'
        end
    end
 
    sourceModule = sourceModule or ''
    if sourceModule ~= '' then
        return '[[Module:' .. sourceModule .. '|Error]]: ' .. msg ..
            category
    end
 
    return msg .. category
end
 
local p = utils
return p
--</nowiki>
Community content is available under CC-BY-SA unless otherwise noted.