<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mbwiki.stairwaygames.work/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AFeature%2FDraft</id>
	<title>Module:Feature/Draft - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mbwiki.stairwaygames.work/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AFeature%2FDraft"/>
	<link rel="alternate" type="text/html" href="https://mbwiki.stairwaygames.work/w/index.php?title=Module:Feature/Draft&amp;action=history"/>
	<updated>2026-04-06T21:07:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://mbwiki.stairwaygames.work/w/index.php?title=Module:Feature/Draft&amp;diff=12215&amp;oldid=prev</id>
		<title>Salty Nori: copied from genshin wiki</title>
		<link rel="alternate" type="text/html" href="https://mbwiki.stairwaygames.work/w/index.php?title=Module:Feature/Draft&amp;diff=12215&amp;oldid=prev"/>
		<updated>2024-01-09T03:35:53Z</updated>

		<summary type="html">&lt;p&gt;copied from genshin wiki&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--- Miscellaneous useful functions.&lt;br /&gt;
local lib = {}&lt;br /&gt;
&lt;br /&gt;
local util = require(&amp;#039;libraryUtil&amp;#039;)&lt;br /&gt;
local checkType = util.checkType&lt;br /&gt;
local checkTypeMulti = util.checkTypeMulti&lt;br /&gt;
local NIL_OK = true&lt;br /&gt;
&lt;br /&gt;
--- Choose one of two values to return.&lt;br /&gt;
--  @param {boolean} cond Determines which value to return.&lt;br /&gt;
--  @param T The value to return if `cond` is true (or truthy).&lt;br /&gt;
--  @param F The value to return if `cond` is false (or falsey).&lt;br /&gt;
function lib.ternary(cond, T, F)&lt;br /&gt;
    if cond then&lt;br /&gt;
        return T&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return F&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Some functions from `mw.text` are slow or may not always work as intended.&lt;br /&gt;
--  This group of functions provides better alternatives for them.&lt;br /&gt;
--  @section `mw.text` replacements&lt;br /&gt;
&lt;br /&gt;
--- &lt;br /&gt;
--  Removes ASCII whitespace from the start and end of `text`.&lt;br /&gt;
--  Slightly more efficient than the `mw.text` implementation.&lt;br /&gt;
--  @see https://help.fandom.com/wiki/Extension:Scribunto#mw.text.trim_is_slow&lt;br /&gt;
--  @param {string} text The text to trim.&lt;br /&gt;
--  @return {string} The trimmed text.&lt;br /&gt;
function lib.trim(text)&lt;br /&gt;
	return (text:gsub( &amp;#039;^[\t\r\n\f ]+&amp;#039;, &amp;#039;&amp;#039; ):gsub( &amp;#039;[\t\r\n\f ]+$&amp;#039;, &amp;#039;&amp;#039; ))&lt;br /&gt;
	-- the &amp;quot;extra&amp;quot; parentheses are important for removing the second value returned from `gsub`&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Returns an iterator over substrings that would be returned by @{lib.split}.&lt;br /&gt;
--  @see @{lib.split} for argument documentation.&lt;br /&gt;
--  @return {function} Iterator over substrings of `text` separated by `delim`.&lt;br /&gt;
function lib.gsplit(text, delim, opt)&lt;br /&gt;
	checkType(&amp;#039;Feature.gsplit&amp;#039;, 1, text, &amp;#039;string&amp;#039;)&lt;br /&gt;
	checkType(&amp;#039;Feature.gsplit&amp;#039;, 2, delim, &amp;#039;string&amp;#039;, NIL_OK)&lt;br /&gt;
	checkType(&amp;#039;Feature.gsplit&amp;#039;, 3, opt, &amp;#039;table&amp;#039;, NIL_OK)&lt;br /&gt;
	if delim == nil then delim = &amp;quot; &amp;quot; end&lt;br /&gt;
	if opt == nil then opt = {} end&lt;br /&gt;
	-- the mediawiki implementation uses ustring, which is slower than string&lt;br /&gt;
	-- and also not necessary if delim isn&amp;#039;t a pattern.&lt;br /&gt;
	-- https://help.fandom.com/wiki/Extension:Scribunto#mw.text.split_is_very_slow&lt;br /&gt;
	&lt;br /&gt;
	-- local g = mw.text.gsplit(text, delim, true)&lt;br /&gt;
	-- local function f()&lt;br /&gt;
	-- 	local value = g()&lt;br /&gt;
	-- 	if value and not opt.noTrim then -- value is nil when the generator ends&lt;br /&gt;
	-- 		value = mw.text.trim(value)&lt;br /&gt;
	-- 	end&lt;br /&gt;
	-- 	if value == &amp;quot;&amp;quot; and opt.removeEmpty then&lt;br /&gt;
	-- 		return f()&lt;br /&gt;
	-- 	end&lt;br /&gt;
	-- 	return value&lt;br /&gt;
	-- end&lt;br /&gt;
	-- return f, nil, nil&lt;br /&gt;
	&lt;br /&gt;
	-- based on https://github.com/wikimedia/mediawiki-extensions-Scribunto/blob/1eecdac6def6418fb36829cc2f20b464c30e4b37/includes/Engines/LuaCommon/lualib/mw.text.lua#L222&lt;br /&gt;
	local s, l = 1, #text&lt;br /&gt;
	local function f()&lt;br /&gt;
		if s then&lt;br /&gt;
			local e, n = string.find( text, delim, s, true )&lt;br /&gt;
			local ret&lt;br /&gt;
			if not e then&lt;br /&gt;
				ret = string.sub( text, s )&lt;br /&gt;
				s = nil&lt;br /&gt;
			elseif n &amp;lt; e then&lt;br /&gt;
				-- Empty separator!&lt;br /&gt;
				ret = string.sub( text, s, e )&lt;br /&gt;
				if e &amp;lt; l then&lt;br /&gt;
					s = e + 1&lt;br /&gt;
				else&lt;br /&gt;
					s = nil&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				ret = e &amp;gt; s and string.sub( text, s, e - 1 ) or &amp;#039;&amp;#039;&lt;br /&gt;
				s = n + 1&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			if not opt.noTrim then&lt;br /&gt;
				ret = lib.trim(ret)&lt;br /&gt;
			end&lt;br /&gt;
			if ret == &amp;#039;&amp;#039; and opt.removeEmpty then&lt;br /&gt;
				return f()&lt;br /&gt;
			end&lt;br /&gt;
			return ret&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return f, nil, nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Returns a table containing the substrings of `text` that are separated by `delim`.&lt;br /&gt;
--  (Compared to @{mw.text.split}, this function always treats `delim` as a literal&lt;br /&gt;
--  string rather than a pattern, and it trims output substrings using @{lib.trim} by default.)&lt;br /&gt;
--  @param       {string} text The string to split.&lt;br /&gt;
--  @param[opt]  {string} delim The delimiter string to use when splitting `text`.&lt;br /&gt;
--                 (Using an empty string will split `text` into individual characters.)&lt;br /&gt;
--  @param[opt]  {table} opt Extra options:&lt;br /&gt;
--  @param[opt]  {boolean} opt.noTrim Set true to disable trimming of generated substrings&lt;br /&gt;
--                  using @{lib.trim}.&lt;br /&gt;
--  @param[opt]  {boolean} opt.removeEmpty Set true to omit empty substrings&lt;br /&gt;
--                  (i.e., when multiple `delim` appear consecutively, or when&lt;br /&gt;
--                  `delim` appears at the start or end of `text`).&lt;br /&gt;
--  @return {table} Substrings of `text separated by `delim`.&lt;br /&gt;
function lib.split(text, delim, opt)&lt;br /&gt;
	checkType(&amp;#039;Feature.split&amp;#039;, 1, text, &amp;#039;string&amp;#039;)&lt;br /&gt;
	checkType(&amp;#039;Feature.split&amp;#039;, 2, delim, &amp;#039;string&amp;#039;, NIL_OK)&lt;br /&gt;
	checkType(&amp;#039;Feature.split&amp;#039;, 3, opt, &amp;#039;table&amp;#039;, NIL_OK)&lt;br /&gt;
	local output = {}&lt;br /&gt;
	for item in lib.gsplit(text, delim, opt) do&lt;br /&gt;
		table.insert(output, item)&lt;br /&gt;
	end&lt;br /&gt;
	return output&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- A wrapper around @{mw.text.unstripNoWiki} that un-escapes&lt;br /&gt;
--  characters/sequences that &amp;lt;nowiki&amp;gt; escapes.&lt;br /&gt;
--  @see https://github.com/wikimedia/mediawiki/blob/c22d01f23b7fe754ef106e97bae32c3966f8db3e/includes/parser/CoreTagHooks.php#L146&lt;br /&gt;
--             for MediaWiki source code for &amp;lt;nowiki&amp;gt;&lt;br /&gt;
function lib.unstripNoWiki(str)&lt;br /&gt;
	return (mw.text.unstripNoWiki(str)&lt;br /&gt;
		:gsub(&amp;#039;&amp;amp;lt;&amp;#039;, &amp;#039;&amp;lt;&amp;#039;):gsub(&amp;#039;&amp;amp;gt;&amp;#039;, &amp;#039;&amp;gt;&amp;#039;)&lt;br /&gt;
		:gsub(&amp;#039;-&amp;amp;#123;&amp;#039;, &amp;#039;-{&amp;#039;):gsub(&amp;#039;&amp;amp;#125;-&amp;#039;, &amp;#039;}-&amp;#039;))&lt;br /&gt;
	-- the &amp;quot;extra&amp;quot; parentheses are important for removing the second value returned from `gsub`&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- @section end&lt;br /&gt;
&lt;br /&gt;
--- Returns an iterator over `tbl` that outputs items in the order defined by `order`.&lt;br /&gt;
--  @param      {table} tbl The table to iterate over.&lt;br /&gt;
--  @param[opt] {table|function} order The iteration order.&lt;br /&gt;
-- &lt;br /&gt;
--                Can be specified either as an ordered list (table) of keys from `tbl`&lt;br /&gt;
--                or as an ordering function that accepts `tbl`, `keyA`, and `keyB`&lt;br /&gt;
--                and returns true when the entry in `tbl` associated wity `keyA`&lt;br /&gt;
--                should come before the one for `keyB`.&lt;br /&gt;
--&lt;br /&gt;
--                If not specified, the keys&amp;#039; natural ordering is used&lt;br /&gt;
--                (i.e., `function(tbl, a, b) return a &amp;lt; b end`).&lt;br /&gt;
--  @return {function} The iterator.&lt;br /&gt;
function lib.spairs(tbl, order)&lt;br /&gt;
	checkType(&amp;#039;Feature.spairs&amp;#039;, 1, tbl, &amp;#039;table&amp;#039;)&lt;br /&gt;
	checkTypeMulti(&amp;#039;Feature.spairs&amp;#039;, 2, order, {&amp;#039;table&amp;#039;, &amp;#039;function&amp;#039;, &amp;#039;nil&amp;#039;})&lt;br /&gt;
	local keys&lt;br /&gt;
	if type(order) == &amp;quot;table&amp;quot; then&lt;br /&gt;
		keys = order&lt;br /&gt;
	else&lt;br /&gt;
	    -- collect the keys&lt;br /&gt;
		keys = {}&lt;br /&gt;
		for k in pairs(tbl) do table.insert(keys, k) end&lt;br /&gt;
		&lt;br /&gt;
		-- sort the keys (using order function if given)&lt;br /&gt;
		if order then&lt;br /&gt;
		    table.sort(keys, function(a, b) return order(tbl, a, b) end)&lt;br /&gt;
		else&lt;br /&gt;
		    table.sort(keys)&lt;br /&gt;
		end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- return the iterator function&lt;br /&gt;
	local i = 0&lt;br /&gt;
	return function()&lt;br /&gt;
		i = i + 1&lt;br /&gt;
		local key = keys[i]&lt;br /&gt;
		return key, tbl[key]&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
	Parses Phantom Template Format strings into a list of maps.&lt;br /&gt;
	@param       {string} input A string formed by concatenating the output of Phantom Templates.&lt;br /&gt;
		Usually, this string is generated by DPL.&lt;br /&gt;
	@param[opt]  {string} key_separator Separator between the entries (key-value pairs) of items in `input`. Defaults to &amp;#039;;&amp;#039;.&lt;br /&gt;
	@param[opt]  {string} end_separator Separator between items in `input`. Defaults to &amp;#039;$&amp;#039;.&lt;br /&gt;
	@param[opt]  {string} equal_separator Separator between the key and value of each entry in `input`. Defaults to &amp;#039;=&amp;#039;.&lt;br /&gt;
	@return      {table} A list of items from `input`; each value is a map of the item&amp;#039;s entries.&lt;br /&gt;
--]]&lt;br /&gt;
function lib.parseTemplateFormat (inputStr, key_separator, end_separator, equal_separator)&lt;br /&gt;
	if key_separator == nil then key_separator = &amp;quot;;&amp;quot; end&lt;br /&gt;
	if end_separator == nil then end_separator = &amp;quot;$&amp;quot; end&lt;br /&gt;
	if equal_separator == nil then equal_separator = &amp;quot;=&amp;quot; end&lt;br /&gt;
	&lt;br /&gt;
	local arg_format = &amp;quot;^%s*(.-)%s*&amp;quot; .. equal_separator .. &amp;quot;%s*(.-)%s*$&amp;quot;&lt;br /&gt;
	&lt;br /&gt;
	local resultTable = {}&lt;br /&gt;
	for str in lib.gsplit(inputStr, end_separator, {noTrim=true, removeEmpty=true}) do&lt;br /&gt;
		local result = {}&lt;br /&gt;
		for param in lib.gsplit(str, key_separator) do&lt;br /&gt;
			local arg, val = param:match(arg_format)&lt;br /&gt;
			if arg then&lt;br /&gt;
				result[arg] = val&lt;br /&gt;
			else&lt;br /&gt;
				-- skip, i guess&lt;br /&gt;
				-- mw.log(&amp;quot;Warning: Lua module found extra &amp;quot; .. key_separator .. &amp;quot; or &amp;quot; .. end_separator .. &amp;quot; separators in DPL output.&amp;quot;)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(resultTable, result)&lt;br /&gt;
	end&lt;br /&gt;
	return resultTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
	Parses Phantom Template Format strings into a list of ordered maps.&lt;br /&gt;
	@param       {string} input A string formed by concatenating the output of Phantom Templates.&lt;br /&gt;
		Usually, this string is generated by DPL.&lt;br /&gt;
	@param[opt]  {string} key_separator Separator between the entries (key-value pairs) of items in `input`. Defaults to &amp;#039;;&amp;#039;.&lt;br /&gt;
	@param[opt]  {string} end_separator Separator between items in `input`. Defaults to &amp;#039;$&amp;#039;.&lt;br /&gt;
	@param[opt]  {string} equal_separator Separator between the key and value of each entry in `input`. Defaults to &amp;#039;=&amp;#039;.&lt;br /&gt;
	@return[name=output] {table} A list of items from `input`; each value is a list of the item&amp;#039;s entries.&lt;br /&gt;
	@return[name=output[i]] {table} The i-th item of `input`.&lt;br /&gt;
	@return[name=output[i].page] {string} The value of the `page` key for this item.&lt;br /&gt;
	@return[name=output[i][j]] {table} The j-th key-value pair of this item.&lt;br /&gt;
	@return[name=output[i][j].key] {string} The j-th key of this item.&lt;br /&gt;
	@return[name=output[i][j].value] The j-th value of this item.&lt;br /&gt;
--]=]&lt;br /&gt;
function lib.parseTemplateFormatOrdered (inputStr, key_separator, end_separator, equal_separator)&lt;br /&gt;
	if key_separator == nil then key_separator = &amp;quot;;&amp;quot; end&lt;br /&gt;
	if end_separator == nil then end_separator = &amp;quot;$&amp;quot; end&lt;br /&gt;
	if equal_separator == nil then equal_separator = &amp;quot;=&amp;quot; end&lt;br /&gt;
	&lt;br /&gt;
	local arg_format = &amp;quot;^%s*(.-)%s*&amp;quot; .. equal_separator .. &amp;quot;%s*(.-)%s*$&amp;quot;&lt;br /&gt;
		&lt;br /&gt;
	local resultTable = {}&lt;br /&gt;
	for str in lib.gsplit(inputStr, end_separator, {noTrim=true, removeEmpty=true}) do&lt;br /&gt;
		local result = {}&lt;br /&gt;
		for param in lib.gsplit(str, key_separator) do&lt;br /&gt;
			local arg, val = param:match(arg_format)&lt;br /&gt;
			if arg == &amp;#039;page&amp;#039; then&lt;br /&gt;
				result[&amp;#039;page&amp;#039;] = val&lt;br /&gt;
			else&lt;br /&gt;
				table.insert(result,{&lt;br /&gt;
					key = arg,&lt;br /&gt;
					value = val&lt;br /&gt;
				})&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		table.insert(resultTable, result)&lt;br /&gt;
	end&lt;br /&gt;
	return resultTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- searches ordered table and returns value&lt;br /&gt;
function lib.orderedTableSearch(tbl, search)&lt;br /&gt;
    for i, obj in ipairs(tbl) do&lt;br /&gt;
        if obj.key == search then&lt;br /&gt;
            return obj.value&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- Add thousands separator to number `n`.&lt;br /&gt;
--  @param {number|frame} n If a frame is given, then its first argument (`frame.args[1]`) will be used as input instead.&lt;br /&gt;
--  @return {string} The number formatted with commas for thousands separators.&lt;br /&gt;
--  @see https://stackoverflow.com/questions/10989788/format-integer-in-lua/10992898#10992898&lt;br /&gt;
function lib.thousandsSeparator(n)&lt;br /&gt;
	if (n == mw.getCurrentFrame()) then&lt;br /&gt;
		n = n.args[1]&lt;br /&gt;
	elseif (type(n) == &amp;quot;table&amp;quot;) then&lt;br /&gt;
		n = n[1]&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local i, j, minus, int, fraction = tostring(n):find(&amp;#039;([-]?)(%d+)([.]?%d*)&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
	-- reverse the int-string and append a comma to all blocks of 3 digits&lt;br /&gt;
	int = int:reverse():gsub(&amp;quot;(%d%d%d)&amp;quot;, &amp;quot;%1,&amp;quot;)&lt;br /&gt;
	&lt;br /&gt;
	-- reverse the int-string back remove an optional comma and put the optional minus and fractional part back&lt;br /&gt;
	return minus .. int:reverse():gsub(&amp;quot;^,&amp;quot;, &amp;quot;&amp;quot;) .. fraction&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- @return {boolean} true iff string or table is empty&lt;br /&gt;
--  @note May not be correct for tables with metatables.&lt;br /&gt;
function lib.isEmpty(item)&lt;br /&gt;
	if item == nil or item == &amp;quot;&amp;quot; then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
	if type(item) == &amp;quot;table&amp;quot; then&lt;br /&gt;
		return next(item) == nil&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- @return {boolean} true iff string or table is not empty&lt;br /&gt;
--  @note May not be correct for tables with metatables.&lt;br /&gt;
function lib.isNotEmpty(item)&lt;br /&gt;
	return not lib.isEmpty(item)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--- @return nil if string or table is empty, otherwise return the value.&lt;br /&gt;
function lib.nilIfEmpty(item)&lt;br /&gt;
	if lib.isEmpty(item) then&lt;br /&gt;
		return nil&lt;br /&gt;
	else&lt;br /&gt;
		return item&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
-- @param {table} t A table of items&lt;br /&gt;
-- @param elm The item to search for&lt;br /&gt;
-- @returns true if `elm` is a value in `t`; false otherwise. (Does not check keys of `t`.)&lt;br /&gt;
-- @see http://stackoverflow.com/q/2282444&lt;br /&gt;
-- @see another implementation: Dev:TableTools.includes()&lt;br /&gt;
function lib.inArray(t, elm)&lt;br /&gt;
    for _, v in pairs(t) do&lt;br /&gt;
        if v == elm then&lt;br /&gt;
            return true&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
    return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return lib&lt;/div&gt;</summary>
		<author><name>Salty Nori</name></author>
	</entry>
</feed>