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