dev
Documentation icon Module documentation
[view] [edit] [history] [purge]

See Global Lua Modules/Feature Miscellaneous useful functions.

Documentation

Package items

lib.ternary(cond, T, F) (function)
Choose one of two values to return.
Parameters:
cond Determines which value to return. (boolean)
T The value to return if cond is true (or truthy).
F The value to return if cond is false (or falsey).
lib.trim(text) (function)
Removes ASCII whitespace from the start and end of text. Slightly more efficient than the mw.text implementation.
Parameter: text The text to trim. (string)
Returns: The trimmed text. (string)
See also: https://help.fandom.com/wiki/Extension:Scribunto#mw.text.trim_is_slow
lib.gsplit() (function)
Returns an iterator over substrings that would be returned by lib. split.
Returns: Iterator over substrings of text separated by delim. (function)
See also: lib.split for argument documentation.
lib.split(text, delim, opt) (function)
Returns a table containing the substrings of text that are separated by delim. (Compared to mw.text.split, this function always treats delim as a literal string rather than a pattern, and it trims output substrings using lib.trim by default.)
Parameters:
text The string to split. (string)
delim The delimiter string to use when splitting text. (Using an empty string will split text into individual characters.) (string; optional)
opt Extra options: (table; optional)
opt.noTrim Set true to disable trimming of generated substrings using lib.trim. (boolean; optional)
opt.removeEmpty Set true to omit empty substrings (i.e., when multiple delim appear consecutively, or when delim appears at the start or end of text). (boolean; optional)
Returns: Substrings of text separated by delim`. (table)
lib.unstripNoWiki() (function)
A wrapper around mw. text.unstripNoWiki that un-escapes characters/sequences that <nowiki> escapes.
See also: https://github.com/wikimedia/mediawiki/blob/c22d01f23b7fe754ef106e97bae32c3966f8db3e/includes/parser/CoreTagHooks.php#L146 for MediaWiki source code for <nowiki>
lib.spairs(tbl, order) (function)
Returns an iterator over tbl that outputs items in the order defined by order.
Parameters:
tbl The table to iterate over. (table)
order The iteration order. Can be specified either as an ordered list (table) of keys from tbl or as an ordering function that accepts tbl, keyA, and keyB and returns true when the entry in tbl associated wity keyA should come before the one for keyB. If not specified, the keys' natural ordering is used (i.e., function(tbl, a, b) return a < b end). (table|function; optional)
Returns: The iterator. (function)
lib.parseTemplateFormat(input, key_separator, end_separator, equal_separator) (function)
Parses Phantom Template Format strings into a list of maps.
Parameters:
input A string formed by concatenating the output of Phantom Templates. Usually, this string is generated by DPL. (string)
key_separator Separator between the entries (key-value pairs) of items in input. Defaults to ';'. (string; optional)
end_separator Separator between items in input. Defaults to '$'. (string; optional)
equal_separator Separator between the key and value of each entry in input. Defaults to '='. (string; optional)
Returns: A list of items from input; each value is a map of the item's entries. (table)
lib.parseTemplateFormatOrdered(input, key_separator, end_separator, equal_separator) (function)
Parses Phantom Template Format strings into a list of ordered maps.
Parameters:
input A string formed by concatenating the output of Phantom Templates. Usually, this string is generated by DPL. (string)
key_separator Separator between the entries (key-value pairs) of items in input. Defaults to ';'. (string; optional)
end_separator Separator between items in input. Defaults to '$'. (string; optional)
equal_separator Separator between the key and value of each entry in input. Defaults to '='. (string; optional)
Returns:
A list of items from input; each value is a list of the item's entries. (table)
The i-th item of input. (table)
The value of the page key for this item. (string)
The j-th key-value pair of this item. (table)
The j-th key of this item. (string)
The j-th value of this item.
lib.thousandsSeparator(n) (function)
Add thousands separator to number n.
Parameter: n If a frame is given, then its first argument (frame.args1 ) will be used as input instead. (number|frame; optional)
Returns: The number formatted with commas for thousands separators. (string)
See also: https://stackoverflow.com/questions/10989788/format-integer-in-lua/10992898#10992898
lib.isEmpty() (function)
Returns: true iff string or table is empty (boolean)
Note: May not be correct for tables with metatables.
lib.isNotEmpty() (function)
Returns: true iff string or table is not empty (boolean)
Note: May not be correct for tables with metatables.
lib.nilIfEmpty() (function)
Returns: nil if string or table is empty, otherwise return the value.
lib.inArray(t, elm) (function)
Parameters:
t A table of items (table)
elm The item to search for
Returns: true if elm is a value in t; false otherwise. (Does not check keys of t.)
See also:
http://stackoverflow.com/q/2282444
another implementation: Dev:TableTools.includes()



--- Miscellaneous useful functions.
local lib = {}

local util = require('libraryUtil')
local checkType = util.checkType
local checkTypeMulti = util.checkTypeMulti
local NIL_OK = true

--- Choose one of two values to return.
--  @param {boolean} cond Determines which value to return.
--  @param T The value to return if `cond` is true (or truthy).
--  @param F The value to return if `cond` is false (or falsey).
function lib.ternary(cond, T, F)
    if cond then
        return T
    end

    return F
end

--- 
--  Removes ASCII whitespace from the start and end of `text`.
--  Slightly more efficient than the `mw.text` implementation.
--  @see https://help.fandom.com/wiki/Extension:Scribunto#mw.text.trim_is_slow
--  @param {string} text The text to trim.
--  @return {string} The trimmed text.
function lib.trim(text)
	return (text:gsub( '^[\t\r\n\f ]+', '' ):gsub( '[\t\r\n\f ]+$', '' ))
	-- the "extra" parentheses are important for removing the second value returned from `gsub`
end


--- Returns an iterator over substrings that would be returned by @{lib.split}.
--  @see @{lib.split} for argument documentation.
--  @return {function} Iterator over substrings of `text` separated by `delim`.
function lib.gsplit(text, delim, opt)
	checkType('Feature.gsplit', 1, text, 'string')
	checkType('Feature.gsplit', 2, delim, 'string', NIL_OK)
	checkType('Feature.gsplit', 3, opt, 'table', NIL_OK)
	if delim == nil then delim = " " end
	if opt == nil then opt = {} end
	-- the mediawiki implementation uses ustring, which is slower than string
	-- and also not necessary if delim isn't a pattern.
	-- https://help.fandom.com/wiki/Extension:Scribunto#mw.text.split_is_very_slow
	
	-- local g = mw.text.gsplit(text, delim, true)
	-- local function f()
	-- 	local value = g()
	-- 	if value and not opt.noTrim then -- value is nil when the generator ends
	-- 		value = mw.text.trim(value)
	-- 	end
	-- 	if value == "" and opt.removeEmpty then
	-- 		return f()
	-- 	end
	-- 	return value
	-- end
	-- return f, nil, nil
	
	-- based on https://github.com/wikimedia/mediawiki-extensions-Scribunto/blob/1eecdac6def6418fb36829cc2f20b464c30e4b37/includes/Engines/LuaCommon/lualib/mw.text.lua#L222
	if delim==';' and text:find('(%&[#%d%w]+);') then
		text = text:gsub('(%&[#%d%w]+);', '%1‡');
	end
	local s, l = 1, #text
	local function f()
		if s then
			local e, n = string.find( text, delim, s, true )
			local ret
			if not e then
				ret = string.sub( text, s )
				s = nil
			elseif n < e then
				-- Empty separator!
				ret = string.sub( text, s, e )
				if e < l then
					s = e + 1
				else
					s = nil
				end
			else
				ret = e > s and string.sub( text, s, e - 1 ) or ''
				s = n + 1
			end
			
			if not opt.noTrim then
				ret = lib.trim(ret)
			end
			if ret == '' and opt.removeEmpty then
				return f()
			end
			if delim==';' and ret:find('(%&[#%d%w]+)‡') then
				ret = ret:gsub('(%&[#%d%w]+)‡', '%1;');
			end
			return ret
		end
	end
	return f, nil, nil
end

--- Returns a table containing the substrings of `text` that are separated by `delim`.
--  (Compared to @{mw.text.split}, this function always treats `delim` as a literal
--  string rather than a pattern, and it trims output substrings using @{lib.trim} by default.)
--  @param       {string} text The string to split.
--  @param[opt]  {string} delim The delimiter string to use when splitting `text`.
--                 (Using an empty string will split `text` into individual characters.)
--  @param[opt]  {table} opt Extra options:
--  @param[opt]  {boolean} opt.noTrim Set true to disable trimming of generated substrings
--                  using @{lib.trim}.
--  @param[opt]  {boolean} opt.removeEmpty Set true to omit empty substrings
--                  (i.e., when multiple `delim` appear consecutively, or when
--                  `delim` appears at the start or end of `text`).
--  @return {table} Substrings of `text separated by `delim`.
function lib.split(text, delim, opt)
	checkType('Feature.split', 1, text, 'string')
	checkType('Feature.split', 2, delim, 'string', NIL_OK)
	checkType('Feature.split', 3, opt, 'table', NIL_OK)
	local output = {}
	for item in lib.gsplit(text, delim, opt) do
		table.insert(output, item)
	end
	return output
end

--- A wrapper around @{mw.text.unstripNoWiki} that un-escapes
--  characters/sequences that &lt;nowiki> escapes.
--  @see https://github.com/wikimedia/mediawiki/blob/c22d01f23b7fe754ef106e97bae32c3966f8db3e/includes/parser/CoreTagHooks.php#L146
--             for MediaWiki source code for &lt;nowiki>
function lib.unstripNoWiki(str)
	return (mw.text.unstripNoWiki(str)
		:gsub('&lt;', '<'):gsub('&gt;', '>')
		:gsub('-&#123;', '-{'):gsub('&#125;-', '}-'))
	-- the "extra" parentheses are important for removing the second value returned from `gsub`
end

--- Returns an iterator over `tbl` that outputs items in the order defined by `order`.
--  @param      {table} tbl The table to iterate over.
--  @param[opt] {table|function} order The iteration order.
-- 
--                Can be specified either as an ordered list (table) of keys from `tbl`
--                or as an ordering function that accepts `tbl`, `keyA`, and `keyB`
--                and returns true when the entry in `tbl` associated wity `keyA`
--                should come before the one for `keyB`.
--
--                If not specified, the keys' natural ordering is used
--                (i.e., `function(tbl, a, b) return a < b end`).
--  @return {function} The iterator.
function lib.spairs(tbl, order)
	checkType('Feature.spairs', 1, tbl, 'table')
	checkTypeMulti('Feature.spairs', 2, order, {'table', 'function', 'nil'})
	local keys
	if type(order) == "table" then
		keys = order
	else
	    -- collect the keys
		keys = {}
		for k in pairs(tbl) do table.insert(keys, k) end
		
		-- sort the keys (using order function if given)
		if order then
		    table.sort(keys, function(a, b) return order(tbl, a, b) end)
		else
		    table.sort(keys)
		end
    end

    -- return the iterator function
	local i = 0
	return function()
		i = i + 1
		local key = keys[i]
		return key, tbl[key]
	end
end

--[[
	Parses Phantom Template Format strings into a list of maps.
	@param       {string} input A string formed by concatenating the output of Phantom Templates.
		Usually, this string is generated by DPL.
	@param[opt]  {string} key_separator Separator between the entries (key-value pairs) of items in `input`. Defaults to ';'.
	@param[opt]  {string} end_separator Separator between items in `input`. Defaults to '$'.
	@param[opt]  {string} equal_separator Separator between the key and value of each entry in `input`. Defaults to '='.
	@return      {table} A list of items from `input`; each value is a map of the item's entries.
--]]
function lib.parseTemplateFormat (inputStr, key_separator, end_separator, equal_separator)
	if key_separator == nil then key_separator = ";" end
	if end_separator == nil then end_separator = "$" end
	if equal_separator == nil then equal_separator = "=" end
	
	local arg_format = "^%s*(.-)%s*" .. equal_separator .. "%s*(.-)%s*$"
	
	local resultTable = {}
	for str in lib.gsplit(inputStr, end_separator, {noTrim=true, removeEmpty=true}) do
		local result = {}
		for param in lib.gsplit(str, key_separator) do
			local arg, val = param:match(arg_format)
			if arg then
				result[arg] = val
			else
				-- skip, i guess
				-- mw.log("Warning: Lua module found extra " .. key_separator .. " or " .. end_separator .. " separators in DPL output.")
			end
		end
		table.insert(resultTable, result)
	end
	return resultTable
end

--[=[
	Parses Phantom Template Format strings into a list of ordered maps.
	@param       {string} input A string formed by concatenating the output of Phantom Templates.
		Usually, this string is generated by DPL.
	@param[opt]  {string} key_separator Separator between the entries (key-value pairs) of items in `input`. Defaults to ';'.
	@param[opt]  {string} end_separator Separator between items in `input`. Defaults to '$'.
	@param[opt]  {string} equal_separator Separator between the key and value of each entry in `input`. Defaults to '='.
	@return[name=output] {table} A list of items from `input`; each value is a list of the item's entries.
	@return[name=output&lsqb;i&rsqb;] {table} The i-th item of `input`.
	@return[name=output&lsqb;i&rsqb;.page] {string} The value of the `page` key for this item.
	@return[name=output&lsqb;i&rsqb;&lsqb;j&rsqb;] {table} The j-th key-value pair of this item.
	@return[name=output&lsqb;i&rsqb;&lsqb;j&rsqb;.key] {string} The j-th key of this item.
	@return[name=output&lsqb;i&rsqb;&lsqb;j&rsqb;.value] The j-th value of this item.
--]=]
function lib.parseTemplateFormatOrdered (inputStr, key_separator, end_separator, equal_separator)
	if key_separator == nil then key_separator = ";" end
	if end_separator == nil then end_separator = "$" end
	if equal_separator == nil then equal_separator = "=" end
	
	local arg_format = "^%s*(.-)%s*" .. equal_separator .. "%s*(.-)%s*$"
		
	local resultTable = {}
	for str in lib.gsplit(inputStr, end_separator, {noTrim=true, removeEmpty=true}) do
		local result = {}
		for param in lib.gsplit(str, key_separator) do
			local arg, val = param:match(arg_format)
			if arg == 'page' then
				result['page'] = val
			else
				table.insert(result,{
					key = arg,
					value = val
				})
			end
		end
		table.insert(resultTable, result)
	end
	return resultTable
end

-- searches ordered table and returns value
function lib.orderedTableSearch(tbl, search)
    for i, obj in ipairs(tbl) do
        if obj.key == search then
            return obj.value
        end
    end
    return false
end

--- Add thousands separator to number `n`.
--  @param {number|frame} n If a frame is given, then its first argument (`frame.args[1]`) will be used as input instead.
--  @return {string} The number formatted with commas for thousands separators.
--  @see https://stackoverflow.com/questions/10989788/format-integer-in-lua/10992898#10992898
function lib.thousandsSeparator(n)
	if (n == mw.getCurrentFrame()) then
		n = n.args[1]
	elseif (type(n) == "table") then
		n = n[1]
	end
	
	local i, j, minus, int, fraction = tostring(n):find('([-]?)(%d+)([.]?%d*)')

	-- reverse the int-string and append a comma to all blocks of 3 digits
	int = int:reverse():gsub("(%d%d%d)", "%1,")
	
	-- reverse the int-string back remove an optional comma and put the optional minus and fractional part back
	return minus .. int:reverse():gsub("^,", "") .. fraction
end


--- @return {boolean} true iff string or table is empty
--  @note May not be correct for tables with metatables.
function lib.isEmpty(item)
	if item == nil or item == "" then
		return true
	end
	if type(item) == "table" then
		return next(item) == nil
	end
	return false
end

--- @return {boolean} true iff string or table is not empty
--  @note May not be correct for tables with metatables.
function lib.isNotEmpty(item)
	return not lib.isEmpty(item)
end

--- @return nil if string or table is empty, otherwise return the value.
function lib.nilIfEmpty(item)
	if lib.isEmpty(item) then
		return nil
	else
		return item
	end
end

---
-- @param {table} t A table of items
-- @param elm The item to search for
-- @returns true if `elm` is a value in `t`; false otherwise. (Does not check keys of `t`.)
-- @see http://stackoverflow.com/q/2282444
-- @see another implementation: Dev:TableTools.includes()
function lib.inArray(t, elm)
    for _, v in pairs(t) do
        if v == elm then
            return true
        end
    end
    return false
end

return lib