Module:Navbox: Difference between revisions

From The Satanic Wiki
Jump to navigation Jump to search
imported>WOSlinker
(remove moz and webkit as standard now well supported)
 
m (1 revision imported)
 
(4 intermediate revisions by 3 users not shown)
Line 1: Line 1:
--------------------------------------------------------------------
--<pre> Navbox Module
--
--
-- This module implements {{Navbox}}
-- * Fully CSS styled (inline styles possible but not default)
-- * Supports unlimited rows
--
--
-- By User:Tjcool007 from layton.fandom.com
--------------------------------------------------------------------


local p = {}
local p = {}


local navbar = require('Module:Navbar')._navbar
local args = {} -- Arguments passed to template
local getArgs -- lazily initialized
local navbox -- Actual navbox


local args
--local working = {}
local border
local rownums, skiprows = {}, {}
local listnums
local hasrows, alt, hasData, isChild = false, false, false, false
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
local activeSection, sections, cimage, cimageleft
local RESTART_MARKER = '\127_ODDEVEN0_\127'
local colspan, rowspan
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'


local function striped(wikitext)
local showText, hideText = 'Show', 'Hide'
-- Return wikitext with markers replaced for odd/even striping.
 
-- Child (subgroup) navboxes are flagged with a category that is removed
local langCode = mw.getContentLanguage():getCode()
-- by parent navboxes. The result is that the category shows all pages
local localization = {} --localized strings table
-- where a child navbox is not contained in a parent navbox.
localization['bn'] = {show = 'দেখান', hide = 'লুকান'}
local orphanCat = '[[Category:Navbox orphans]]'
localization['de'] = {show = 'Ausklappen', hide = 'Einklappen'}
if border == 'subgroup' and args.orphan ~= 'yes' then
localization['en'] = {show = 'Show', hide = 'Hide'}
-- No change; striping occurs in outermost navbox.
localization['es'] = {show = 'Mostrar', hide = 'Ocultar'}
return wikitext .. orphanCat
localization['hi'] = {show = 'दिखाएँ', hide = 'छिपाएँ'}
localization['ja'] = {show = '表示', hide = '隠す'}
localization['ru'] = {show = 'показать', hide = 'скрыть'}
localization['zh'] = {show = '显示', hide = '隐藏'}
if localization[langCode] then
    showText = localization[langCode]['show']
    hideText = localization[langCode]['hide']
end
 
------------------------------------------------
-- Title
------------------------------------------------
 
--- Processes the VDE links in the title
--
-- @param titlecell The table cell of the title
local function processVde( titlecell )
if not args.template then return end
 
titlecell:wikitext('<span class="navbox-vde">'
.. mw.getCurrentFrame():expandTemplate({
title = 'vdelinks',
args = { args.template, ['type'] = 'navbox' }
}) .. '</span>')
end
 
--- Processes the main title row
local function processTitle()
local titlerow = mw.html.create('tr'):addClass('navbox-title')
local titlecell = mw.html.create('th'):attr('colspan',colspan):attr('scope','col')
 
if not pcall( processVde, titlecell ) then
titlecell:wikitext( '<b class="navbox-vde error" title="Missing Template:Vdelinks">!!!</b>' )
end
end
local first, second = 'odd', 'even'
 
if args.evenodd then
titlecell:wikitext( args.title or '{{{title}}}' )
if args.evenodd == 'swap' then
 
first, second = second, first
-- Padding
local hasTemplate = args.template ~= nil
local hasState = not args.state or args.state ~= 'plain'
 
if hasTemplate ~= hasState then
if hasTemplate then
titlecell:addClass('navbox-title-padright')
else
else
first = args.evenodd
titlecell:addClass('navbox-title-padleft')
second = first
end
end
end
end
local changer
 
if first == second then
if args.titleclass then titlerow:addClass( args.titleclass ) end
changer = first
if args.titlestyle then titlecell:cssText( args.titlestyle ) end
else
 
local index = 0
titlerow:node(titlecell)
changer = function (code)
navbox:node(titlerow)
if code == '0' then
-- Current occurrence is for a group before a nested table.
-- Set it to first as a valid although pointless class.
-- The next occurrence will be the first row after a title
-- in a subgroup and will also be first.
index = 0
return first
end
index = index + 1
return index % 2 == 1 and first or second
end
end
local regex = orphanCat:gsub('([%[%]])', '%%%1')
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer))  -- () omits gsub count
end
end


local function processItem(item, nowrapitems)
local function _addGutter( parent, incRowspan )
if item:sub(1, 2) == '{|' then
parent:tag('tr'):addClass('navbox-gutter'):tag('td'):attr('colspan',2)
-- Applying nowrap to lines in a table does not make sense.
 
-- Add newlines to compensate for trim of x in |parm=x in a template.
if incRowspan then
return '\n' .. item ..'\n'
rowspan = rowspan + 1
end
if nowrapitems == 'yes' then
local lines = {}
for line in (item .. '\n'):gmatch('([^\n]*)\n') do
local prefix, content = line:match('^([*:;#]+)%s*(.*)')
if prefix and not content:match('^<span class="nowrap">') then
line = prefix .. '<span class="nowrap">' .. content .. '</span>'
end
table.insert(lines, line)
end
item = table.concat(lines, '\n')
end
if item:match('^[*:;#]') then
return '\n' .. item ..'\n'
end
end
return item
end
end


local function renderNavBar(titleCell)
------------------------------------------------
-- Above/Below
------------------------------------------------
 
--- Processes the above and below rows
--
-- @param rowtype Either 'above' or 'below'
local function processAboveBelow( rowtype )
if not args[rowtype] then return end


if args.navbar ~= 'off' and args.navbar ~= 'plain' and not (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox') then
local abrow = mw.html.create('tr'):addClass('navbox-'..rowtype)
titleCell:wikitext(navbar{
local abcell = mw.html.create('td'):attr('colspan',colspan):wikitext( args[rowtype] )
args.name,
 
mini = 1,
if args[rowtype .. 'class'] then abrow:addClass( args[rowtype .. 'class'] ) end
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;box-shadow:none;padding:0;'
if args[rowtype .. 'style'] then abcell:cssText( args[rowtype .. 'style'] ) end
})
end


abrow:node( abcell )
_addGutter( navbox )
navbox:node( abrow )
end
end


--
------------------------------------------------
--   Title row
-- Main Rows
--
------------------------------------------------
local function renderTitleRow(tbl)
 
if not args.title then return end
--- Processes the images
local function _processImage(row, imgtype)
if not args[imgtype] then return end
 
local iclass = imgtype == 'image' and 'navbox-image-right' or 'navbox-image-left'
 
local imagecell = mw.html.create('td'):addClass('navbox-image'):addClass(iclass)
 
local image = args[imgtype]
if image:sub(1,1) ~= '[' then
local width = args[imgtype .. 'width'] or '100px'
imagecell:css('width',width):wikitext('['..'[' .. image  .. '|' .. width .. '|link=' .. (args[imgtype .. 'link'] or '') .. ']]')
else
imagecell:css('width','0%'):wikitext(image)
end


local titleRow = tbl:tag('tr')
if args[imgtype .. 'class'] then imagecell:addClass( args[imgtype .. 'class'] ) end
if args[imgtype .. 'style'] then imagecell:cssText( args[imgtype .. 'style'] ) end


if args.titlegroup then
row:node( imagecell )
titleRow
if imgtype == 'image' then
:tag('th')
cimage = imagecell
:attr('scope', 'row')
else
:addClass('navbox-group')
cimageleft = imagecell
:addClass(args.titlegroupclass)
:cssText(args.basestyle)
:cssText(args.groupstyle)
:cssText(args.titlegroupstyle)
:wikitext(args.titlegroup)
end
end
end
--- Closes the currently active section (if any)
local function _closeCurrentSection()
if not activeSection then return end


local titleCell = titleRow:tag('th'):attr('scope', 'col')
local row = mw.html.create('tr'):addClass('navbox-section-row')
local cell = mw.html.create('td'):attr('colspan',2)


if args.titlegroup then
if not hasrows then
titleCell
_processImage(row,'imageleft')
:css('border-left', '2px solid #fdfdfd')
:css('width', '100%')
end
end


local titleColspan = 2
cell:node(sections[activeSection])
if args.imageleft then titleColspan = titleColspan + 1 end
row:node(cell)
if args.image then titleColspan = titleColspan + 1 end
if args.titlegroup then titleColspan = titleColspan - 1 end


titleCell
local firstRow = false
:cssText(args.basestyle)
if not hasrows then
:cssText(args.titlestyle)
firstRow = true
:addClass('navbox-title')
hasrows = true
:attr('colspan', titleColspan)
_processImage(row,'image')
end


renderNavBar(titleCell)
_addGutter(navbox,not firstRow)
navbox:node(row)
rowspan = rowspan + 1


titleCell
activeSection = false
:tag('div')
hasData = false
-- id for aria-labelledby attribute
:attr('id', mw.uri.anchorEncode(args.title))
:addClass(args.titleclass)
:css('font-size', '114%')
:css('margin', '0 4em')
:wikitext(processItem(args.title))
end
end


--- Handles alternating rows
--
--
--   Above/Below rows
-- @return Alternatingly returns true or false. Always returns false if alternating rows
--        are disabled with "alternaterows = no"
local function _alternateRow()
if args.alternaterows == 'no' then return false end
if alt then
alt = false
return true
else
alt = true
return false
end
end
 
--- Process a single Header "row"
--
--
-- @param num Number of the row to be processed
local function processHeader(num)
if not args['header'..num] then return end
_closeCurrentSection()


local function getAboveBelowColspan()
local subtable = mw.html.create('table'):addClass('navbox-section')
local ret = 2
local headerrow = mw.html.create('tr')
if args.imageleft then ret = ret + 1 end
local header = mw.html.create('th'):addClass('navbox-header'):attr('colspan',2):attr('scope','col'):wikitext( args['header'..num] )
if args.image then ret = ret + 1 end
 
return ret
local collapseme = args['state'..num] or false
end
local state = false
 
if collapseme then
-- Look at this one
if collapseme ~= 'plain' then
state = collapseme == 'expanded' and 'expanded' or 'collapsed'
end
else
-- Look at default
local collapseall = args.defaultstate or false
if collapseall then
state = collapseall == 'expanded' and 'expanded' or 'collapsed'
end
end


local function renderAboveRow(tbl)
if state then
if not args.above then return end
subtable:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'..num] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'..num] or args['defaultcollapsetext'] or hideText)
if state == 'collapsed' then
subtable:addClass('mw-collapsed')
end
header:addClass('navbox-header-collapsible')
end


tbl:tag('tr')
if args.headerclass then headerrow:addClass( args.headerclass ) end
:tag('td')
if args.headerstyle then header:cssText( args.headerstyle ) end
:addClass('navbox-abovebelow')
:addClass(args.aboveclass)
:cssText(args.basestyle)
:cssText(args.abovestyle)
:attr('colspan', getAboveBelowColspan())
:tag('div')
-- id for aria-labelledby attribute, if no title
:attr('id', args.title and nil or mw.uri.anchorEncode(args.above))
:wikitext(processItem(args.above, args.nowrapitems))
end


local function renderBelowRow(tbl)
headerrow:node(header)
if not args.below then return end
subtable:node(headerrow)


tbl:tag('tr')
sections[num] = subtable
:tag('td')
activeSection = num
:addClass('navbox-abovebelow')
:addClass(args.belowclass)
:cssText(args.basestyle)
:cssText(args.belowstyle)
:attr('colspan', getAboveBelowColspan())
:tag('div')
:wikitext(processItem(args.below, args.nowrapitems))
end
end


--- Processes a single list row
--
--
--   List rows
-- @param num Number of the row to be processed
--
local function processList(num)
local function renderListRow(tbl, index, listnum)
if not args['list'..num] then return end
local row = tbl:tag('tr')
 
local row = mw.html.create('tr'):addClass('navbox-row')


if index == 1 and args.imageleft then
if not hasrows and not activeSection then
row
_processImage(row, 'imageleft')
:tag('td')
:addClass('noviewer')
:addClass('navbox-image')
:addClass(args.imageclass)
:css('width', '1px')              -- Minimize width
:css('padding', '0px 2px 0px 0px')
:cssText(args.imageleftstyle)
:attr('rowspan', #listnums)
:tag('div')
:wikitext(processItem(args.imageleft))
end
end


if args['group' .. listnum] then
local listcell = mw.html.create('td'):addClass('navbox-list')
local groupCell = row:tag('th')
local hlistcell = listcell:tag('div'):addClass('hlist')
local data = args['list'..num]
if data:sub(1,1) == '*' then
-- Add newlines to support lists properly
hlistcell
:newline()
:wikitext( data )
:newline()
else
hlistcell:wikitext( data )
end


-- id for aria-labelledby attribute, if lone group with no title or above
local altRow = _alternateRow()
if listnum == 1 and not (args.title or args.above or args.group2) then
if altRow then
groupCell
row:addClass( args.altrowclass or 'alt' )
:attr('id', mw.uri.anchorEncode(args.group1))
end


groupCell
local listclass = args.altlistclass or args.listclass or false
:attr('scope', 'row')
if listclass then listcell:addClass( listclass ) end
:addClass('navbox-group')
:addClass(args.groupclass)
:cssText(args.basestyle)
:css('width', args.groupwidth or '1%') -- If groupwidth not specified, minimize width


groupCell
local liststyle = args.altliststyle or args.liststyle or false
:cssText(args.groupstyle)
if liststyle then listcell:cssText( liststyle ) end
:cssText(args['group' .. listnum .. 'style'])
else
:wikitext(args['group' .. listnum])
if args.rowclass then row:addClass( args.rowclass ) end
if args.listclass then listcell:addClass( args.listclass ) end
if args.liststyle then listcell:cssText( args.liststyle ) end
end
end


local listCell = row:tag('td')
if args['group'..num] then
local groupcell = mw.html.create('th'):addClass('navbox-group'):attr('scope','row'):wikitext( args['group'..num] )
 
if altRow then
local groupclass = args.altgroupclass or args.groupclass or false
if groupclass then groupcell:addClass( groupclass ) end
local groupstyle = args.altgroupstyle or args.groupstyle or false
if groupstyle then groupcell:cssText( groupstyle ) end
else
if args.groupclass then groupcell:addClass( args.groupclass ) end
if args.groupstyle then groupcell:cssText( args.groupstyle ) end
end


if args['group' .. listnum] then
row:node( groupcell )
listCell
:css('text-align', 'left')
:css('border-left-width', '2px')
:css('border-left-style', 'solid')
else
else
listCell:attr('colspan', 2)
listcell:attr('colspan',2):addClass('no-group')
end
end


if not args.groupwidth then
row:node( listcell )
listCell:css('width', '100%')
 
local firstRow = false
if not hasrows and not activeSection then
firstRow = true
hasrows = true
_processImage(row, 'image')
end
end


local rowstyle  -- usually nil so cssText(rowstyle) usually adds nothing
if activeSection then
if index % 2 == 1 then
local parent = sections[activeSection]
rowstyle = args.oddstyle
if not isChild or not firstRow then
_addGutter(parent)
end
parent:node(row)
hasData = true
else
else
rowstyle = args.evenstyle
if not isChild or not firstRow then
_addGutter(navbox,not firstRow)
end
navbox:node( row )
rowspan = rowspan + 1
end
end
end


local listText = args['list' .. listnum]
--- Processes all rows
local oddEven = ODD_EVEN_MARKER
local function processRows()
if listText:sub(1, 12) == '</div><table' then
sections = {}
-- Assume list text is for a subgroup navbox so no automatic striping for this row.
for i=1,#rownums do
oddEven = listText:find('<th[^>]*"navbox%-title"') and RESTART_MARKER or 'odd'
local num = rownums[i]
if not skiprows[num] then
processHeader(num)
processList(num)
end
end
end
listCell
_closeCurrentSection()
:css('padding', '0px')
:cssText(args.liststyle)
if cimageleft then
:cssText(rowstyle)
cimageleft:attr('rowspan',rowspan)
:cssText(args['list' .. listnum .. 'style'])
end
:addClass('navbox-list')
if cimage then
:addClass('navbox-' .. oddEven)
cimage:attr('rowspan',rowspan)
:addClass(args.listclass)
:addClass(args['list' .. listnum .. 'class'])
:tag('div')
:css('padding', (index == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
:wikitext(processItem(listText, args.nowrapitems))
 
if index == 1 and args.image then
row
:tag('td')
:addClass('noviewer')
:addClass('navbox-image')
:addClass(args.imageclass)
:css('width', '1px')              -- Minimize width
:css('padding', '0px 0px 0px 2px')
:cssText(args.imagestyle)
:attr('rowspan', #listnums)
:tag('div')
:wikitext(processItem(args.image))
end
end
end
end


------------------------------------------------
-- ARGUMENTS PREPROCESSOR
-- * Extracts arguments from frame and stores them in args table
-- * At the same time, checks for valid row numbers
------------------------------------------------


--- Preprocessor for the arguments.
-- Will fill up the args table with the parameters from the frame grouped by their type.
--
--
--   Tracking categories
-- @param frame The frame passed to the Module.
--
local function preProcessArgs(frame)
local tmp = {}


local function needsHorizontalLists()
if frame == mw.getCurrentFrame() then
if border == 'subgroup' or args.tracking == 'no' then
tmp = frame:getParent().args
return false
else
tmp = frame
end
end
local listClasses = {
['plainlist'] = true, ['hlist'] = true, ['hlist hnum'] = true,
['hlist hwrap'] = true, ['hlist vcard'] = true, ['vcard hlist'] = true,
['hlist vevent'] = true,
}
return not (listClasses[args.listclass] or listClasses[args.bodyclass])
end


local function hasBackgroundColors()
-- Storage tables
for _, key in ipairs({'titlestyle', 'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
local nums = {}
if tostring(args[key]):find('background', 1, true) then
return true
end
end
end
 
local function hasBorders()
for _, key in ipairs({'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
if tostring(args[key]):find('border', 1, true) then
return true
end
end
end


local function isIllegible()
-- Loop over all the args
local styleratio = require('Module:Color contrast')._styleratio
for k,v in pairs(tmp) do
-- Skip empty args, which are useless
if v ~= '' then
local cat,num = tostring(k):match('^(%a+)([1-9]%d*)$')


for key, style in pairs(args) do
if cat == 'header' or cat == 'list' then
if tostring(key):match("style$") then
nums[num] = true
if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then
return true
end
end
args[k] = v -- Simple copy
end
end
end
end
return false
end


local function getTrackingCategories()
colspan = args.image and 3 or 2
local cats = {}
if args.imageleft then colspan = colspan + 1 end
if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
rowspan = 0
if hasBackgroundColors() then table.insert(cats, 'Navboxes using background colours') end
if isIllegible() then table.insert(cats, 'Potentially illegible navboxes') end
if hasBorders() then table.insert(cats, 'Navboxes using borders') end
return cats
end


local function renderTrackingCategories(builder)
if args.alternaterows == 'swap' then
local title = mw.title.getCurrentTitle()
alt = true
if title.namespace ~= 10 then return end -- not in template space
end
local subpage = title.subpageText
if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end


for _, cat in ipairs(getTrackingCategories()) do
for k, v in pairs(nums) do
builder:wikitext('[[Category:' .. cat .. ']]')
rownums[#rownums+1] = tonumber(k)
end
end
end


--
table.sort(rownums)
--  Main navbox tables
--
local function renderMainTable()
local tbl = mw.html.create('table')
:addClass('nowraplinks')
:addClass(args.bodyclass)


if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
-- Calculate skip rows
if args.state == 'collapsed' then args.state = 'mw-collapsed' end
local cSection, cSkip
tbl
local showall = args.showall
:addClass('mw-collapsible')
for i=1,#rownums do
:addClass(args.state or 'autocollapse')
local num = rownums[i]
if args['header'..num] then
cSection = true
cSkip = false
local showme = args['show'..num]
if showme == 'no' then
cSkip = true
elseif showme == 'auto' or (showme ~= 'yes' and showall ~= 'yes') then
if not args['list'..num] then
local nextNum = rownums[i+1]
cSkip = not nextNum or args['header'..nextNum] -- If next has a header -> skip
end
end
end
if cSection and cSkip then
skiprows[num] = true
end
end
end
end


tbl:css('border-spacing', 0)
------------------------------------------------
if border == 'subgroup' or border == 'none' then
-- MAIN FUNCTIONS
tbl
------------------------------------------------
:addClass('navbox-subgroup')
:cssText(args.bodystyle)
:cssText(args.style)
else  -- regular navbox - bodystyle and style will be applied to the wrapper table
tbl
:addClass('navbox-inner')
:css('background', 'transparent')
:css('color', 'inherit')
end
tbl:cssText(args.innerstyle)


renderTitleRow(tbl)
--- Processes the arguments to create the navbox.
renderAboveRow(tbl)
--
for i, listnum in ipairs(listnums) do
-- @return A string with HTML that is the navbox.
renderListRow(tbl, i, listnum)
local function _navbox()
-- Create the root HTML element
local trim = function(s)
return s and mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") or ''
end
end
renderBelowRow(tbl)
local border = args.border or trim(args[1])  or ''
isChild = (border == 'child' or border == 'subgroup')


return tbl
if isChild then
end
navbox = mw.html.create('table'):addClass('navbox-subgroup')
else
navbox = mw.html.create('table'):addClass('navbox')


function p._navbox(navboxArgs)
if args.state ~= 'plain' then
args = navboxArgs
navbox:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'] or args['defaultcollapsetext'] or hideText)
listnums = {}
if args.state == 'collapsed' then
 
navbox:addClass('mw-collapsed')
for k, _ in pairs(args) do
end
if type(k) == 'string' then
local listnum = k:match('^list(%d+)$')
if listnum then table.insert(listnums, tonumber(listnum)) end
end
end
end
end
table.sort(listnums)


border = mw.text.trim(args.border or args[1] or '')
if args.bodyclass then navbox:addClass(args.bodyclass) end
if border == 'child' then
if args.bodystyle then navbox:cssText(args.bodystyle) end
border = 'subgroup'
end


-- render the main body of the navbox
-- Process...
local tbl = renderMainTable()
if not isChild then
processTitle()
processAboveBelow('above')
processRows()
processAboveBelow('below')


-- render the appropriate wrapper around the navbox, depending on the border param
return tostring(navbox)
local res = mw.html.create()
if border == 'none' then
local nav = res:tag('div')
:attr('role', 'navigation')
:node(tbl)
-- aria-labelledby title, otherwise above, otherwise lone group
if args.title or args.above or (args.group1 and not args.group2) then
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title or args.above or args.group1))
else
nav:attr('aria-label', 'Navbox')
end
elseif border == 'subgroup' then
-- We assume that this navbox is being rendered in a list cell of a parent navbox, and is
-- therefore inside a div with padding:0em 0.25em. We start with a </div> to avoid the
-- padding being applied, and at the end add a <div> to balance out the parent's </div>
res
:wikitext('</div>')
:node(tbl)
:wikitext('<div>')
else
else
local nav = res:tag('div')
processRows()
:attr('role', 'navigation')
:addClass('navbox')
:addClass(args.navboxclass)
:cssText(args.bodystyle)
:cssText(args.style)
:css('padding', '3px')
:node(tbl)
-- aria-labelledby title, otherwise above, otherwise lone group
if args.title or args.above or (args.group1 and not args.group2) then
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title or args.above or args.group1))
else
nav:attr('aria-label', 'Navbox')
end
end


if (args.nocat or 'false'):lower() == 'false' then
local wrapper = mw.html.create('')
renderTrackingCategories(res)
wrapper:wikitext('</div>')
wrapper:node(navbox)
wrapper:wikitext('<div class="hlist">')
return tostring(wrapper)
end
end
return striped(tostring(res))
end
end


function p.navbox(frame)
--- Main module entry point.
if not getArgs then
-- To be called with {{#invoke:navbox|main}} or directly from another module.
getArgs = require('Module:Arguments').getArgs
--
end
-- @param frame The frame passed to the module via the #invoke. If called from another
args = getArgs(frame, {wrappers = {'Template:Navbox'}})
--              module directly, this should be a table with the parameter definition.
 
function p.main(frame)
-- Read the arguments in the order they'll be output in, to make references number in the right order.
-- Save the arguments in a local variable so other functions can use them.
local _
preProcessArgs(frame)
_ = args.title
_ = args.above
return _navbox()
for i = 1, 20 do
_ = args["group" .. tostring(i)]
_ = args["list" .. tostring(i)]
end
_ = args.below
 
return p._navbox(args)
end
end


return p
return p

Latest revision as of 02:23, 30 April 2021

This module is invoked by the {{Navbox}} template. Navbox templates are not displayed for mobile users.


--------------------------------------------------------------------
--<pre> Navbox Module
--
-- * Fully CSS styled (inline styles possible but not default)
-- * Supports unlimited rows
--
-- By User:Tjcool007 from layton.fandom.com
--------------------------------------------------------------------

local p = {}

local args = {} -- Arguments passed to template
local navbox -- Actual navbox

--local working = {}
local rownums, skiprows = {}, {}
local hasrows, alt, hasData, isChild = false, false, false, false
local activeSection, sections, cimage, cimageleft
local colspan, rowspan

local showText, hideText = 'Show', 'Hide'

local langCode = mw.getContentLanguage():getCode()
local localization = {} --localized strings table
localization['bn'] = {show = 'দেখান', hide = 'লুকান'}
localization['de'] = {show = 'Ausklappen', hide = 'Einklappen'}
localization['en'] = {show = 'Show', hide = 'Hide'}
localization['es'] = {show = 'Mostrar', hide = 'Ocultar'}
localization['hi'] = {show = 'दिखाएँ', hide = 'छिपाएँ'}
localization['ja'] = {show = '表示', hide = '隠す'}
localization['ru'] = {show = 'показать', hide = 'скрыть'}
localization['zh'] = {show = '显示', hide = '隐藏'}
if localization[langCode] then
    showText = localization[langCode]['show']
    hideText = localization[langCode]['hide']
end

------------------------------------------------
-- Title
------------------------------------------------

--- Processes the VDE links in the title
--
-- @param titlecell The table cell of the title
local function processVde( titlecell )
	if not args.template then return end

	titlecell:wikitext('<span class="navbox-vde">'
		.. mw.getCurrentFrame():expandTemplate({
			title = 'vdelinks',
			args = { args.template, ['type'] = 'navbox' }
		}) .. '</span>')
end

--- Processes the main title row
local function processTitle()
	local titlerow = mw.html.create('tr'):addClass('navbox-title')
	local titlecell = mw.html.create('th'):attr('colspan',colspan):attr('scope','col')

	if not pcall( processVde, titlecell ) then
		titlecell:wikitext( '<b class="navbox-vde error" title="Missing Template:Vdelinks">!!!</b>' )
	end

	titlecell:wikitext( args.title or '{{{title}}}' )

	-- Padding
	local hasTemplate = args.template ~= nil
	local hasState = not args.state or args.state ~= 'plain'

	if hasTemplate ~= hasState then
		if hasTemplate then
			titlecell:addClass('navbox-title-padright')
		else
			titlecell:addClass('navbox-title-padleft')
		end
	end

	if args.titleclass then titlerow:addClass( args.titleclass ) end
	if args.titlestyle then titlecell:cssText( args.titlestyle ) end

	titlerow:node(titlecell)
	navbox:node(titlerow)
end

local function _addGutter( parent, incRowspan )
	parent:tag('tr'):addClass('navbox-gutter'):tag('td'):attr('colspan',2)

	if incRowspan then
		rowspan = rowspan + 1
	end
end

------------------------------------------------
-- Above/Below
------------------------------------------------

--- Processes the above and below rows
--
-- @param rowtype Either 'above' or 'below'
local function processAboveBelow( rowtype )
	if not args[rowtype] then return end

	local abrow = mw.html.create('tr'):addClass('navbox-'..rowtype)
	local abcell = mw.html.create('td'):attr('colspan',colspan):wikitext( args[rowtype] )

	if args[rowtype .. 'class'] then abrow:addClass( args[rowtype .. 'class'] ) end
	if args[rowtype .. 'style'] then abcell:cssText( args[rowtype .. 'style'] ) end

	abrow:node( abcell )
	_addGutter( navbox )
	navbox:node( abrow )
end

------------------------------------------------
-- Main Rows
------------------------------------------------

--- Processes the images
local function _processImage(row, imgtype)
	if not args[imgtype] then return end

	local iclass = imgtype == 'image' and 'navbox-image-right' or 'navbox-image-left'

	local imagecell = mw.html.create('td'):addClass('navbox-image'):addClass(iclass)

	local image = args[imgtype]
	if image:sub(1,1) ~= '[' then
		local width = args[imgtype .. 'width'] or '100px'
		imagecell:css('width',width):wikitext('['..'[' .. image  .. '|' .. width .. '|link=' .. (args[imgtype .. 'link'] or '') .. ']]')
	else
		imagecell:css('width','0%'):wikitext(image)
	end

	if args[imgtype .. 'class'] then imagecell:addClass( args[imgtype .. 'class'] ) end
	if args[imgtype .. 'style'] then imagecell:cssText( args[imgtype .. 'style'] ) end

	row:node( imagecell )
	if imgtype == 'image' then
		cimage = imagecell
	else
		cimageleft = imagecell
	end
end

--- Closes the currently active section (if any)
local function _closeCurrentSection()
	if not activeSection then return end

	local row = mw.html.create('tr'):addClass('navbox-section-row')
	local cell = mw.html.create('td'):attr('colspan',2)

	if not hasrows then
		_processImage(row,'imageleft')	
	end

	cell:node(sections[activeSection])
	row:node(cell)

	local firstRow = false
	if not hasrows then
		firstRow = true
		hasrows = true
		_processImage(row,'image')	
	end

	_addGutter(navbox,not firstRow)
	navbox:node(row)	
	rowspan = rowspan + 1

	activeSection = false
	hasData = false
end

--- Handles alternating rows
--
-- @return Alternatingly returns true or false. Always returns false if alternating rows
--         are disabled with "alternaterows = no"
local function _alternateRow()
	if args.alternaterows == 'no' then return false end
	if alt then
		alt = false
		return true
	else
		alt = true
		return false
	end
end

--- Process a single Header "row"
--
-- @param num Number of the row to be processed
local function processHeader(num)
	if not args['header'..num] then return end

	_closeCurrentSection()

	local subtable = mw.html.create('table'):addClass('navbox-section')
	local headerrow = mw.html.create('tr')
	local header = mw.html.create('th'):addClass('navbox-header'):attr('colspan',2):attr('scope','col'):wikitext( args['header'..num] )

	local collapseme = args['state'..num] or false
	local state = false

	if collapseme then
		-- Look at this one
		if collapseme ~= 'plain' then
			state = collapseme == 'expanded' and 'expanded' or 'collapsed'
		end
	else
		-- Look at default 
		local collapseall = args.defaultstate or false
		if collapseall then
			state = collapseall == 'expanded' and 'expanded' or 'collapsed'	
		end
	end

	if state then
		subtable:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'..num] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'..num] or args['defaultcollapsetext'] or hideText)
		if state == 'collapsed' then
			subtable:addClass('mw-collapsed')	
		end
		header:addClass('navbox-header-collapsible')
	end

	if args.headerclass then headerrow:addClass( args.headerclass ) end
	if args.headerstyle then header:cssText( args.headerstyle ) end

	headerrow:node(header)	
	subtable:node(headerrow)

	sections[num] = subtable
	activeSection = num
end

--- Processes a single list row
--
-- @param num Number of the row to be processed
local function processList(num)	
	if not args['list'..num] then return end

	local row = mw.html.create('tr'):addClass('navbox-row')

	if not hasrows and not activeSection then
		_processImage(row, 'imageleft')	
	end

	local listcell = mw.html.create('td'):addClass('navbox-list')
	local hlistcell = listcell:tag('div'):addClass('hlist')
	
	local data = args['list'..num]
	
	if data:sub(1,1) == '*' then
		-- Add newlines to support lists properly
		hlistcell
			:newline()
			:wikitext( data )
			:newline()
	else
		hlistcell:wikitext( data )
	end

	local altRow = _alternateRow()
	if altRow then
		row:addClass( args.altrowclass or 'alt' )

		local listclass = args.altlistclass or args.listclass or false
		if listclass then listcell:addClass( listclass ) end

		local liststyle = args.altliststyle or args.liststyle or false
		if liststyle then listcell:cssText( liststyle ) end
	else
		if args.rowclass then row:addClass( args.rowclass ) end
		if args.listclass then listcell:addClass( args.listclass ) end
		if args.liststyle then listcell:cssText( args.liststyle ) end
	end

	if args['group'..num] then
		local groupcell = mw.html.create('th'):addClass('navbox-group'):attr('scope','row'):wikitext( args['group'..num] )

		if altRow then
			local groupclass = args.altgroupclass or args.groupclass or false
			if groupclass then groupcell:addClass( groupclass ) end
 
			local groupstyle = args.altgroupstyle or args.groupstyle or false
			if groupstyle then groupcell:cssText( groupstyle ) end
		else	
			if args.groupclass then groupcell:addClass( args.groupclass ) end
			if args.groupstyle then groupcell:cssText( args.groupstyle ) end
		end

		row:node( groupcell )
	else
		listcell:attr('colspan',2):addClass('no-group')
	end

	row:node( listcell )

	local firstRow = false
	if not hasrows and not activeSection then
		firstRow = true
		hasrows = true
		_processImage(row, 'image')
	end

	if activeSection then
		local parent = sections[activeSection]
		if not isChild or not firstRow then
			_addGutter(parent)
		end
		parent:node(row)
		hasData = true
	else
		if not isChild or not firstRow then
			_addGutter(navbox,not firstRow)
		end
		navbox:node( row )
		rowspan = rowspan + 1
	end
end

--- Processes all rows
local function processRows()
	sections = {}
	for i=1,#rownums do
		local num = rownums[i]
		if not skiprows[num] then
			processHeader(num)
			processList(num)
		end
	end
	_closeCurrentSection()
 
	if cimageleft then
		cimageleft:attr('rowspan',rowspan)		
	end
	if cimage then
		cimage:attr('rowspan',rowspan)
	end
end

------------------------------------------------
-- ARGUMENTS PREPROCESSOR
-- * Extracts arguments from frame and stores them in args table
-- * At the same time, checks for valid row numbers
------------------------------------------------

--- Preprocessor for the arguments.
-- Will fill up the args table with the parameters from the frame grouped by their type.
--
-- @param frame The frame passed to the Module.
local function preProcessArgs(frame)
	local tmp = {}

	if frame == mw.getCurrentFrame() then
		tmp = frame:getParent().args
	else
		tmp = frame
	end

	-- Storage tables
	local nums = {}

	-- Loop over all the args
	for k,v in pairs(tmp) do
		-- Skip empty args, which are useless
		if v ~= '' then
			local cat,num = tostring(k):match('^(%a+)([1-9]%d*)$')

			if cat == 'header' or cat == 'list' then
				nums[num] = true
			end

			args[k] = v -- Simple copy
		end
	end

	colspan = args.image and 3 or 2
	if args.imageleft then colspan = colspan + 1 end
	rowspan = 0

	if args.alternaterows == 'swap' then
		alt = true
	end

	for k, v in pairs(nums) do
		rownums[#rownums+1] = tonumber(k)
	end

	table.sort(rownums)

	-- Calculate skip rows
	local cSection, cSkip
	local showall = args.showall
	for i=1,#rownums do
		local num = rownums[i]
		if args['header'..num] then
			cSection = true
			cSkip = false
			local showme = args['show'..num]
			if showme == 'no' then
				cSkip = true
			elseif showme == 'auto' or (showme ~= 'yes' and showall ~= 'yes') then
				if not args['list'..num] then
					local nextNum = rownums[i+1]
					cSkip = not nextNum or args['header'..nextNum] -- If next has a header -> skip
				end
			end
		end
		if cSection and cSkip then
			skiprows[num] = true
		end
	end
end

------------------------------------------------
-- MAIN FUNCTIONS
------------------------------------------------

--- Processes the arguments to create the navbox.
--
-- @return A string with HTML that is the navbox.
local function _navbox()
	-- Create the root HTML element
	local trim = function(s)
		return s and mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1") or ''
	end
	local border = args.border or trim(args[1])  or ''
	isChild = (border == 'child' or border == 'subgroup')

	if isChild then
		navbox = mw.html.create('table'):addClass('navbox-subgroup')
	else
		navbox = mw.html.create('table'):addClass('navbox')

		if args.state ~= 'plain' then
			navbox:addClass('mw-collapsible'):attr('data-expandtext',args['expandtext'] or args['defaultexpandtext'] or showText):attr('data-collapsetext',args['collapsetext'] or args['defaultcollapsetext'] or hideText)
			if args.state == 'collapsed' then
				navbox:addClass('mw-collapsed')
			end
		end
	end

 	if args.bodyclass then navbox:addClass(args.bodyclass) end
	if args.bodystyle then navbox:cssText(args.bodystyle) end

	-- Process...
	if not isChild then
		processTitle()
		processAboveBelow('above')
		processRows()
		processAboveBelow('below')

		return tostring(navbox)
	else
		processRows()

		local wrapper = mw.html.create('')
		wrapper:wikitext('</div>')
		wrapper:node(navbox)
		wrapper:wikitext('<div class="hlist">')
		return tostring(wrapper)
	end
end

--- Main module entry point.
-- To be called with {{#invoke:navbox|main}} or directly from another module.
--
-- @param frame The frame passed to the module via the #invoke. If called from another
--              module directly, this should be a table with the parameter definition.
function p.main(frame)
	-- Save the arguments in a local variable so other functions can use them.
	preProcessArgs(frame)
 
	return _navbox()
end

return p