Module:Sandbox/LVL: Difference between revisions

LVL (talk | contribs)
No edit summary
LVL (talk | contribs)
No edit summary
Line 1: Line 1:
-- Single-file Tabs module for deadlock.wiki
-- All dependencies inlined to avoid Lua.import
-- Inlined Module:Array (partial stub - expand if needed)
local Array = {}
Array.forEach = function(elements, funct)
for index, element in ipairs(elements) do
funct(element, index)
end
end
Array.any = function(tbl, predicate)
for _, v in ipairs(tbl) do
if predicate(v) then return true end
end
return false
end
Array.all = function(tbl, predicate)
for _, v in ipairs(tbl) do
if not predicate(v) then return false end
end
return true
end
-- Inlined Module:Class
local Class = {}
function Class.export(tbl, opts)
local result = {}
for _, name in ipairs(opts.exports) do
result[name] = tbl[name]
end
return result
end
-- Inlined Module:Logic
local Logic = {}
function Logic.readBool(val)
return val == true or val == 'true' or val == '1'
end
function Logic.readBoolOrNil(val)
if val == nil then return nil end
return Logic.readBool(val)
end
function Logic.isEmpty(val)
return val == nil or val == ''
end
function Logic.isNotEmpty(val)
return not Logic.isEmpty(val)
end
function Logic.nilOr(val, fallback)
if val == nil then return fallback else return val end
end
-- Inlined Module:Operator
local Operator = {}
function Operator.property(prop)
return function(tbl)
return tbl and tbl[prop]
end
end
-- Inlined Module:Page
local Page = {}
function Page.makeInternalLink(_, text, link)
return string.format('[[%s|%s]]', link, text)
end
-- Inlined Module:Table
local Table = {}
function Table.extract(tbl, key)
local val = tbl[key]
if val ~= '' then return val end
return nil
end
-- Begin Tabs Module
local Tabs = {}
local Tabs = {}


-- Helper functions
function Tabs.static(args)
local function readBool(val)
args = args or {}
    if val == nil then return false end
local tabArgs = Tabs._readArguments(args, {allowThis2 = true})
    if type(val) == 'boolean' then return val end
local tabCount = #tabArgs
    if type(val) == 'string' then
if tabCount == 0 then return end
        val = val:lower()
 
        return val == 'true' or val == 'yes' or val == '1'
Tabs._setThis(tabArgs)
    end
local tabs = mw.html.create('ul')
    return false
:attr('class', 'nav nav-tabs navigation-not-searchable tabs tabs' .. tabCount)
:attr('data-nosnippet')
 
local subTabs = mw.html.create()
 
Array.forEach(tabArgs, function(tab)
local name = tab.name or Tabs._getDisplayNameFromLink(tab.link)
local text = tab.link and Page.makeInternalLink({}, name, tab.link) or tab.name
tabs:tag('li'):addClass(tab.this and 'active' or nil):wikitext(text)
subTabs:node(tab.this and tab.tabs or nil)
end)
 
return mw.html.create()
:tag('div')
:addClass('tabs-static')
:attr('data-nosnippet', '')
:node(tabs)
:done()
:node(subTabs)
end
 
function Tabs.dynamic(args)
args = args or {}
local tabArgs = Tabs._readArguments(args, {removeEmptyTabs = Logic.readBool(args.removeEmptyTabs)})
local tabCount = #tabArgs
if tabCount == 0 then return end
 
local hasContent = Array.all(tabArgs, function(tab) return Logic.isNotEmpty(tab.content) end)
local allEmpty = Array.all(tabArgs, function(tab) return Logic.isEmpty(tab.content) end)
assert(hasContent or allEmpty, 'Some of the tabs have contents while others do not')
 
local isSingular = tabCount == 1 and hasContent
if isSingular and not Logic.readBool(args.showSingularAsTab) then
return Tabs._single(tabArgs[1], not Logic.readBool(args.suppressHeader))
end
 
local tabs = mw.html.create('ul'):addClass('nav nav-tabs tabs tabs' .. tabCount)
if not Array.any(tabArgs, Operator.property('this')) then
tabArgs[1].this = true
end
 
local build = function(obj, elementType, content, class, isActive)
local element = mw.html.create(elementType)
:addClass(class)
:addClass(isActive and 'active' or nil)
:newline()
:node(content)
obj:newline():node(element)
end
 
Array.forEach(tabArgs, function(tabData, tabIndex)
build(tabs, 'li', tabData.name, 'tab' .. tabIndex, tabData.this)
end)
 
if not Logic.nilOr(Logic.readBoolOrNil(args['hide-showall']), isSingular) then
tabs:tag('li'):addClass('show-all'):wikitext('Show All')
end
 
tabs:newline()
 
local contents = Tabs._buildContentDiv(hasContent, Logic.readBool(args['hybrid-tabs']), Logic.readBool(args['no-padding']))
 
if not hasContent then
return '<div class="tabs-dynamic navigation-not-searchable" data-nosnippet>\n' .. tostring(tabs) .. contents
end
 
Array.forEach(tabArgs, function(tabData, tabIndex)
build(contents, 'div', tabData.content, 'content' .. tabIndex, tabData.this)
end)
 
return mw.html.create('div')
:addClass('tabs-dynamic navigation-not-searchable')
:attr('data-nosnippet')
:node(tabs)
:newline()
:node(contents)
end
 
function Tabs._readArguments(args, options)
local tabArgs = {}
local tabIndex = 1
local this = tonumber(args.This)
local this2 = tonumber(args.This2)
 
while args['name' .. tabIndex] or args['link' .. tabIndex] do
if args['content' .. tabIndex] or not options.removeEmptyTabs then
table.insert(tabArgs, {
name = Table.extract(args, 'name' .. tabIndex),
link = Table.extract(args, 'link' .. tabIndex),
content = Table.extract(args, 'content' .. tabIndex),
tabs = Table.extract(args, 'tabs' .. tabIndex),
this = this == tabIndex or (options.allowThis2 and this2 == tabIndex),
})
end
tabIndex = tabIndex + 1
end
 
if Logic.readBool(args.returnIfEmpty) then
return tabArgs
end
 
assert(Logic.isNotEmpty(tabArgs), 'You are trying to add a "Tabs" template without arguments for names nor links')
return tabArgs
end
 
function Tabs._setThis(tabArgs)
if Array.any(tabArgs, Operator.property('this')) then return end
 
local fullPageName = mw.title.getCurrentTitle().prefixedText
local this
local maxLinkLength = -1
 
Array.forEach(tabArgs, function (tab, tabIndex)
local link = tab.link
if not link then return end
link = link:gsub('_', ' ')
local linkLength = #link
local charAfter = fullPageName:sub(linkLength + 1, linkLength + 1)
local pagePartial = fullPageName:sub(1, linkLength)
if pagePartial == link and (charAfter == '/' or charAfter == '') and linkLength > maxLinkLength then
maxLinkLength = linkLength
this = tabIndex
end
end)
 
if this then tabArgs[this].this = true end
end
end


local function getDisplayNameFromLink(link)
function Tabs._buildContentDiv(hasContent, hybridTabs, noPadding)
    return link:match('([^/]+)$') or link
if hasContent then
local contentDiv = mw.html.create('div'):addClass('tabs-content')
if hybridTabs then
contentDiv:css('border-style', 'none !important'):css('padding', '0 !important')
elseif noPadding then
contentDiv:css('padding', '0 !important')
end
return contentDiv
end
 
local style = ''
if hybridTabs then style = 'border-style:none !important; padding:0 !important;'
elseif noPadding then style = 'padding:0 !important;' end
return '\n<div class="tabs-content" style="' .. style .. '">' end
 
function Tabs._single(tab, showHeader)
local header
if showHeader then
header = mw.html.create():tag('h6'):wikitext(tab.name):done():newline()
end
return mw.html.create():node(header):node(tab.content)
end
end


-- Main function
function Tabs._getDisplayNameFromLink(link)
function Tabs.static(frame)
local parts = mw.text.split(link, '/', true)
    local args = frame.args or {}
return parts[#parts]
   
    -- Find all tabs
    local tabs = {}
    local i = 1
    while args['name' .. i] or args['link' .. i] do
        table.insert(tabs, {
            name = args['name' .. i],
            link = args['link' .. i],
            this = (tonumber(args.This) == i)
        })
        i = i + 1
    end
   
    if #tabs == 0 then
        return 'Error: No tabs defined (need at least name1 or link1)'
    end
   
    -- Auto-set first tab as active if none specified
    if not args.This then
        tabs[1].this = true
    end
   
    -- Build HTML
    local container = mw.html.create('div')
        :addClass('tabs-static')
   
    local tablist = mw.html.create('ul')
        :addClass('nav nav-tabs tabs tabs' .. #tabs)
   
    for _, tab in ipairs(tabs) do
        local display = tab.name or getDisplayNameFromLink(tab.link)
        local text = tab.link and ('[[' .. tab.link .. '|' .. display .. ']]') or display
       
        tablist:tag('li')
            :addClass(tab.this and 'active' or nil)
            :wikitext(text)
    end
   
    return container:node(tablist)
end
end


return Tabs
return {
  static = Tabs.static,
  dynamic = Tabs.dynamic,
}