<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.pathfindersonline.org/index.php?action=history&amp;feed=atom&amp;title=Module%3ATabs</id>
	<title>Module:Tabs - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.pathfindersonline.org/index.php?action=history&amp;feed=atom&amp;title=Module%3ATabs"/>
	<link rel="alternate" type="text/html" href="https://wiki.pathfindersonline.org/index.php?title=Module:Tabs&amp;action=history"/>
	<updated>2026-04-06T01:26:02Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.5</generator>
	<entry>
		<id>https://wiki.pathfindersonline.org/index.php?title=Module:Tabs&amp;diff=562556&amp;oldid=prev</id>
		<title>DesignerThan: Created page with &quot;local p = {} local args = {} local origArgs = {}  local function deleteDuplicates(array) 	local hash = {} 	local res = {} 	for _,v in ipairs(array) do 	   if (not hash[v]) the...&quot;</title>
		<link rel="alternate" type="text/html" href="https://wiki.pathfindersonline.org/index.php?title=Module:Tabs&amp;diff=562556&amp;oldid=prev"/>
		<updated>2022-01-30T14:43:34Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;local p = {} local args = {} local origArgs = {}  local function deleteDuplicates(array) 	local hash = {} 	local res = {} 	for _,v in ipairs(array) do 	   if (not hash[v]) the...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local p = {}&lt;br /&gt;
local args = {}&lt;br /&gt;
local origArgs = {}&lt;br /&gt;
&lt;br /&gt;
local function deleteDuplicates(array)&lt;br /&gt;
	local hash = {}&lt;br /&gt;
	local res = {}&lt;br /&gt;
	for _,v in ipairs(array) do&lt;br /&gt;
	   if (not hash[v]) then&lt;br /&gt;
	       res[#res+1] = v -- you could print here instead of saving to result table if you wanted&lt;br /&gt;
	       hash[v] = true&lt;br /&gt;
	   end&lt;br /&gt;
	end&lt;br /&gt;
	return res&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Returns a table containing the numbers of the arguments that exist&lt;br /&gt;
-- for the specified prefix. For example, if the prefix was 'data', and&lt;br /&gt;
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.&lt;br /&gt;
local function getArgNums(prefix)&lt;br /&gt;
	local nums = {}&lt;br /&gt;
	for k, v in pairs(args) do&lt;br /&gt;
		local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')&lt;br /&gt;
		if num then table.insert(nums, tonumber(num)) end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(nums)&lt;br /&gt;
	return nums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getTableArgNums(prefix)&lt;br /&gt;
	local nums = {}&lt;br /&gt;
	for k, v in pairs(args) do&lt;br /&gt;
		local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)')&lt;br /&gt;
		if num then table.insert(nums, tonumber(num)) end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(nums)&lt;br /&gt;
	return deleteDuplicates(nums)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderTab( wikiLink )&lt;br /&gt;
	local HTMLCode = mw.html.create( 'li' )&lt;br /&gt;
	HTMLCode&lt;br /&gt;
		:addClass( 'navtabs-item' )&lt;br /&gt;
		:wikitext( wikiLink )&lt;br /&gt;
	return tostring( HTMLCode )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderDropdownSeperator()&lt;br /&gt;
	HTMLSeperator = mw.html.create( 'div' )&lt;br /&gt;
	HTMLSeperator&lt;br /&gt;
		:addClass('dropdown-divider')&lt;br /&gt;
	return HTMLSeperator&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderDropdownTab(tabNum)&lt;br /&gt;
	local prefix			= 'dropdown'..tabNum..''&lt;br /&gt;
	local linkNums			= getArgNums(prefix..'_link')&lt;br /&gt;
	local linkSeperatorNums	= getArgNums(prefix..'_seperatorLink')&lt;br /&gt;
	local retHTMLCode		= ''&lt;br /&gt;
	&lt;br /&gt;
	if #linkNums == 0 then&lt;br /&gt;
		error('Invalid dropdown configuration. No links defined!')&lt;br /&gt;
	elseif #linkNums == 1 then&lt;br /&gt;
		retHTMLCode = renderTab(args[prefix..'_link'..linkNums[1]])&lt;br /&gt;
	else&lt;br /&gt;
		local HTMLTabParent = mw.html.create( 'li' )&lt;br /&gt;
		local buttonArgs = {}&lt;br /&gt;
		buttonArgs['tagname'] = 'button'&lt;br /&gt;
		buttonArgs['class'] = 'dropdown-toggle'&lt;br /&gt;
		-- do the selflink with JS Module&lt;br /&gt;
		buttonArgs['type'] = 'button'&lt;br /&gt;
		if args[prefix..'_CSSID'] then&lt;br /&gt;
			buttonArgs['id'] = 'dropdown'..args[prefix..'_CSSID']..'Keys'&lt;br /&gt;
		end&lt;br /&gt;
		buttonArgs['data-toggle'] = 'dropdown'&lt;br /&gt;
		buttonArgs['aria-haspopup'] = 'true'&lt;br /&gt;
		buttonArgs['aria-expanded'] = 'false'&lt;br /&gt;
		buttonText = args[prefix..'_btnText']&lt;br /&gt;
		local frame = mw.getCurrentFrame()&lt;br /&gt;
		local dropdownButton = frame:extensionTag('htmltag', buttonText, buttonArgs)&lt;br /&gt;
		HTMLTabParent&lt;br /&gt;
			:addClass( 'navtabs-item' )&lt;br /&gt;
			:addClass( 'dropdown' )&lt;br /&gt;
			:wikitext( dropdownButton )&lt;br /&gt;
		&lt;br /&gt;
		HTMLVariantsContainer = mw.html.create( 'div' )&lt;br /&gt;
		HTMLVariantsContainer:addClass( 'dropdown-menu' )&lt;br /&gt;
		if args[prefix..'_CSSID'] then&lt;br /&gt;
			HTMLVariantsContainer:attr( 'aria-labelledby', 'dropdown'..args[prefix..'_CSSID']..'Keys')&lt;br /&gt;
		end&lt;br /&gt;
		-- add link's to the dropdown menu&lt;br /&gt;
		for key, num in pairs(linkNums) do&lt;br /&gt;
			if (args[prefix..'_seperatorLink'..num] == 'before') then&lt;br /&gt;
				HTMLVariantsContainer:node( renderDropdownSeperator() )&lt;br /&gt;
			end&lt;br /&gt;
			HTMLVariantLink = mw.html.create( 'span' )&lt;br /&gt;
			HTMLVariantLink&lt;br /&gt;
				:addClass( 'dropdown-item' )&lt;br /&gt;
				:wikitext( args[prefix..'_link'..num] )&lt;br /&gt;
				:done()&lt;br /&gt;
			HTMLVariantsContainer:node( HTMLVariantLink )&lt;br /&gt;
			if (args[prefix..'_seperatorLink'..num] == 'after') then&lt;br /&gt;
				HTMLVariantsContainer:node( renderDropdownSeperator() )&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		HTMLVariantsContainer:done()&lt;br /&gt;
		HTMLTabParent:node( HTMLVariantsContainer )&lt;br /&gt;
		retHTMLCode = tostring( HTMLTabParent )&lt;br /&gt;
	end&lt;br /&gt;
	return retHTMLCode&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function renderTabs()&lt;br /&gt;
	-- get configured tab's&lt;br /&gt;
	local tabSimpleNum = getArgNums('simple')&lt;br /&gt;
	local tabDropNum = getTableArgNums('dropdown')&lt;br /&gt;
	-- check which tab's should be shown&lt;br /&gt;
	local displayTabs = {}&lt;br /&gt;
	for simpleI, simpleNum in ipairs(tabSimpleNum) do&lt;br /&gt;
		for dropI, dropNum in ipairs(tabDropNum) do&lt;br /&gt;
			if simpleNum == dropNum and args['dropdown'..tostring(dropNum)..'_btnText'] and&lt;br /&gt;
				args['dropdown'..tostring(dropNum)..'_CSSID'] then&lt;br /&gt;
				displayTabs[simpleI] = {&lt;br /&gt;
					tabType = 'dropdown',&lt;br /&gt;
					tabNum = dropNum&lt;br /&gt;
				}&lt;br /&gt;
				mw.log('Tabs.renderTabs: Simple tab replaced by Dropdown with number: '..dropNum )&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if not displayTabs[simpleI] then&lt;br /&gt;
			displayTabs[simpleI] = {&lt;br /&gt;
				tabType = 'simple',&lt;br /&gt;
				tabNum = simpleNum&lt;br /&gt;
			}&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local strTabs = ''&lt;br /&gt;
	for k, dispTab in ipairs(displayTabs) do&lt;br /&gt;
		if (dispTab.tabType == 'simple') then&lt;br /&gt;
			strTabs = strTabs .. tostring(renderTab(args['simple' .. tostring(dispTab.tabNum)]))&lt;br /&gt;
		elseif (dispTab.tabType == 'dropdown') then&lt;br /&gt;
			strTabs = strTabs .. tostring(renderDropdownTab(dispTab.tabNum))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return strTabs&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function _tabbar()&lt;br /&gt;
	local tabContainer = mw.html.create( 'ul' )&lt;br /&gt;
	tabContainer&lt;br /&gt;
		:attr( 'id', 'navtabs' )&lt;br /&gt;
		:attr( 'class', 'noprint')&lt;br /&gt;
		:wikitext( renderTabs() )&lt;br /&gt;
	return tostring( tabContainer )&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- If the argument exists and isn't blank, add it to the argument table.&lt;br /&gt;
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.&lt;br /&gt;
local function preprocessSingleArg(argName)&lt;br /&gt;
	mw.log('Tabs:preprocessSingleArg: '..argName)&lt;br /&gt;
	if origArgs[argName] and origArgs[argName] ~= '' then&lt;br /&gt;
		args[argName] = origArgs[argName]&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Assign the parameters with the given prefixes to the args table, in order, in&lt;br /&gt;
-- batches of the step size specified. This is to prevent references etc. from&lt;br /&gt;
-- appearing in the wrong order. The prefixTable should be an array containing&lt;br /&gt;
-- tables, each of which has two possible fields, a &amp;quot;prefix&amp;quot; string and a&lt;br /&gt;
-- &amp;quot;depend&amp;quot; table. The function always parses parameters containing the &amp;quot;prefix&amp;quot;&lt;br /&gt;
-- string, but only parses parameters in the &amp;quot;depend&amp;quot; table if the prefix&lt;br /&gt;
-- parameter is present and non-blank.&lt;br /&gt;
local function preprocessArgs(prefixTable, step)&lt;br /&gt;
	if type(prefixTable) ~= 'table' then&lt;br /&gt;
		error(&amp;quot;Non-table value detected for the prefix table&amp;quot;, 2)&lt;br /&gt;
	end&lt;br /&gt;
	if type(step) ~= 'number' then&lt;br /&gt;
		error(&amp;quot;Invalid step value detected&amp;quot;, 2)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Get arguments without a number suffix, and check for bad input.&lt;br /&gt;
	for i,v in ipairs(prefixTable) do&lt;br /&gt;
		if type(v) ~= 'table' or type(v.prefix) ~= &amp;quot;string&amp;quot; or&lt;br /&gt;
			(v.depend and type(v.depend) ~= 'table') then&lt;br /&gt;
			error('Invalid input detected to preprocessArgs prefix table', 2)&lt;br /&gt;
		end&lt;br /&gt;
		preprocessSingleArg(v.prefix)&lt;br /&gt;
		-- Only parse the depend parameter if the prefix parameter is present&lt;br /&gt;
		-- and not blank.&lt;br /&gt;
		if args[v.prefix] and v.depend then&lt;br /&gt;
			for j, dependValue in ipairs(v.depend) do&lt;br /&gt;
				if type(dependValue) ~= 'string' then&lt;br /&gt;
					error('Invalid &amp;quot;depend&amp;quot; parameter value detected in preprocessArgs')&lt;br /&gt;
				end&lt;br /&gt;
				preprocessSingleArg(dependValue)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Get arguments with number suffixes.&lt;br /&gt;
	local a = 1 -- Counter variable.&lt;br /&gt;
	local moreArgumentsExist = true&lt;br /&gt;
	while moreArgumentsExist == true do&lt;br /&gt;
		moreArgumentsExist = false&lt;br /&gt;
		for i = a, a + step - 1 do&lt;br /&gt;
			for j,v in ipairs(prefixTable) do&lt;br /&gt;
				local prefixArgName = v.prefix .. tostring(i)&lt;br /&gt;
				if origArgs[prefixArgName] then&lt;br /&gt;
					-- Do another loop if any arguments are found, even blank ones.&lt;br /&gt;
					moreArgumentsExist = true&lt;br /&gt;
					preprocessSingleArg(prefixArgName)&lt;br /&gt;
				end&lt;br /&gt;
				-- Process the depend table if the prefix argument is present&lt;br /&gt;
				-- and not blank, or we are processing &amp;quot;prefix1&amp;quot; and &amp;quot;prefix&amp;quot; is&lt;br /&gt;
				-- present and not blank, and if the depend table is present.&lt;br /&gt;
				if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then&lt;br /&gt;
					for j,dependValue in ipairs(v.depend) do&lt;br /&gt;
						local dependArgName = dependValue .. tostring(i)&lt;br /&gt;
						preprocessSingleArg(dependArgName)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		a = a + step&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function preprocessTableArgs(prefixTable, step)&lt;br /&gt;
	if type(prefixTable) ~= 'table' then&lt;br /&gt;
		error(&amp;quot;Non-table value detected for the prefix table&amp;quot;, 2)&lt;br /&gt;
	end&lt;br /&gt;
	if type(step) ~= 'number' then&lt;br /&gt;
		error(&amp;quot;Invalid step value detected&amp;quot;, 2)&lt;br /&gt;
	end&lt;br /&gt;
	if (not(prefixTable.tablePrefix) and (type(prefixTable.tablePrefix) ~= 'string')) then&lt;br /&gt;
		error(&amp;quot;Invalid tablePrefix value detected&amp;quot;, 2)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- search for all arguement's with tablePrefix&lt;br /&gt;
	local a = 1 -- Counter variable.&lt;br /&gt;
	local moreArgumentsExist = true&lt;br /&gt;
	while moreArgumentsExist == true do&lt;br /&gt;
		moreArgumentsExist = false&lt;br /&gt;
		for i = a, a + step - 1 do&lt;br /&gt;
			local prefixTableName = prefixTable.tablePrefix .. tostring(i)&lt;br /&gt;
			local tablePrefixExists = false&lt;br /&gt;
			for key, args in pairs(origArgs) do&lt;br /&gt;
				if string.find(key, prefixTableName) then&lt;br /&gt;
					-- there is atleast one argument with the table index&lt;br /&gt;
					moreArgumentsExist = true&lt;br /&gt;
					tablePrefixExists = true&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if tablePrefixExists then&lt;br /&gt;
				if (prefixTable.tableSingleArgs and type(prefixTable.tableSingleArgs) == 'table') then&lt;br /&gt;
					for key, single in ipairs(prefixTable.tableSingleArgs) do&lt;br /&gt;
						if (not single.prefix or type(single.prefix) ~= 'string') then&lt;br /&gt;
							error(&amp;quot;Invalid tableSingleArg value detected!&amp;quot;)&lt;br /&gt;
						end&lt;br /&gt;
						preprocessSingleArg(prefixTableName..'_'..single.prefix)&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					error('Invalid type of tableSingleArgs!')&lt;br /&gt;
				end&lt;br /&gt;
				if (prefixTable.tableArgs and type(prefixTable.tableArgs) == 'table') then&lt;br /&gt;
					for key, mArgs in ipairs(prefixTable.tableArgs) do&lt;br /&gt;
						if (mArgs and type(mArgs) == 'table' and&lt;br /&gt;
							mArgs.prefix and type(mArgs.prefix) == 'string') then&lt;br /&gt;
							local locArgs = {}&lt;br /&gt;
							if (not(mArgs.prefix) or type(mArgs.prefix) ~= 'string') then&lt;br /&gt;
								error('Invalid type of mArgs.prefix!')&lt;br /&gt;
							end&lt;br /&gt;
							locArgs.prefix = prefixTableName..'_'..mArgs.prefix&lt;br /&gt;
							locArgs.depend = {}&lt;br /&gt;
							if mArgs.depend and type(mArgs.depend) == 'table' then&lt;br /&gt;
								for depIndex, depValue in ipairs(mArgs.depend) do&lt;br /&gt;
									table.insert(locArgs.depend, prefixTableName..'_'..depValue)&lt;br /&gt;
								end&lt;br /&gt;
							end&lt;br /&gt;
							preprocessArgs({locArgs}, step)&lt;br /&gt;
						else&lt;br /&gt;
							error('Invalid mArgs Argument!')&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					error('Invalid type of tableArgs!')&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		a = a + step&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Parse the data parameters in the same order that the old {{tabbar}} did, so&lt;br /&gt;
-- that references etc. will display in the expected places. Parameters that&lt;br /&gt;
-- depend on another parameter are only processed if that parameter is present,&lt;br /&gt;
-- to avoid phantom references appearing in article reference lists.&lt;br /&gt;
local function parseDataParameters()&lt;br /&gt;
	-- preprocessSingleArg('')&lt;br /&gt;
	preprocessArgs({&lt;br /&gt;
		{prefix = 'simple'}&lt;br /&gt;
	}, 10)&lt;br /&gt;
	preprocessTableArgs({&lt;br /&gt;
		tablePrefix = 'dropdown',&lt;br /&gt;
		tableSingleArgs = {&lt;br /&gt;
			{prefix = 'btnText'},&lt;br /&gt;
			{prefix = 'CSSID'}&lt;br /&gt;
		},&lt;br /&gt;
		tableArgs = {&lt;br /&gt;
			{prefix = 'link', depend = {'seperatorLink'}}&lt;br /&gt;
		}&lt;br /&gt;
	}, 10)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- If called via #invoke, use the args passed into the invoking template.&lt;br /&gt;
-- Otherwise, for testing purposes, assume args are being passed directly in.&lt;br /&gt;
function p.tabbar(frame)&lt;br /&gt;
	mw.log('Tabs.tabbar: Start!')&lt;br /&gt;
	if frame == mw.getCurrentFrame() then&lt;br /&gt;
		origArgs = frame:getParent().args&lt;br /&gt;
	else&lt;br /&gt;
		origArgs = frame&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	parseDataParameters()&lt;br /&gt;
	&lt;br /&gt;
	return _tabbar()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- For calling via #invoke within a template&lt;br /&gt;
function p.tabbarTemplate(frame)&lt;br /&gt;
	mw.log('Tabs.tabbarTemplate: Start!')&lt;br /&gt;
	origArgs = {}&lt;br /&gt;
	for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end&lt;br /&gt;
	&lt;br /&gt;
	parseDataParameters()&lt;br /&gt;
	&lt;br /&gt;
	return _tabbar()&lt;br /&gt;
end&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>DesignerThan</name></author>
	</entry>
</feed>