Module:Navbox: Difference between revisions

From Podpedia
Content added Content deleted
m (1 revision imported)
(The revision 17691 by Frietjes(talk) has been undone.)
Line 1: Line 1:
--------------------------------------------------------------------
-- Navbox Module
--
--
-- * Fully CSS styled (inline styles possible but not default)
-- This module implements {{Navbox}}
-- * Supports unlimited rows
--
--
-- By User:Tjcool007 from layton.wikia.com

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


local rownums, skiprows = {}, {}
local navbar = require('Module:Navbar')._navbar
local hasrows, alt, hasData, isChild = false, false, false, false
local getArgs -- lazily initialized
local activeSection, sections, cimage, cimageleft

local args
local colspan, rowspan
local border
local listnums = {}
local showText, hideText = 'Show', 'Hide'
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
------------------------------------------------
local RESTART_MARKER = '\127_ODDEVEN0_\127'
-- Title
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'
------------------------------------------------

local function striped(wikitext)
--- Processes the VDE links in the title
-- Return wikitext with markers replaced for odd/even striping.
--
-- Child (subgroup) navboxes are flagged with a category that is removed
-- by parent navboxes. The result is that the category shows all pages
-- @param titlecell The table cell of the title
local function processVde( titlecell )
-- where a child navbox is not contained in a parent navbox.
if not args.template then return end
local orphanCat = '[[Category:Navbox orphans]]'
if border == 'subgroup' and args.orphan ~= 'yes' then
titlecell:wikitext('<span class="navbox-vde">'
-- No change; striping occurs in outermost navbox.
.. mw.getCurrentFrame():expandTemplate({
return wikitext .. orphanCat
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'
titlecell:wikitext( args.title or '{{{title}}}' )
if args.evenodd then
if args.evenodd == 'swap' then
-- Padding
first, second = second, first
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
titlecell:addClass('navbox-title-padleft')
first = args.evenodd
second = first
end
end
end
end
local changer
if args.titleclass then titlerow:addClass( args.titleclass ) end
if first == second then
if args.titlestyle then titlecell:cssText( args.titlestyle ) end
changer = first
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'
else
else
imagecell:css('width','0%'):wikitext(image)
local index = 0
end
changer = function (code)
if code == '0' then
if args[imgtype .. 'class'] then imagecell:addClass( args[imgtype .. 'class'] ) end
-- Current occurrence is for a group before a nested table.
if args[imgtype .. 'style'] then imagecell:cssText( args[imgtype .. 'style'] ) end
-- Set it to first as a valid although pointless class.
-- The next occurrence will be the first row after a title
row:node( imagecell )
-- in a subgroup and will also be first.
if imgtype == 'image' then
index = 0
cimage = imagecell
return first
else
end
index = index + 1
cimageleft = imagecell
return index % 2 == 1 and first or second
end
end
end
local regex = orphanCat:gsub('([%[%]])', '%%%1')
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer)) -- () omits gsub count
end
end

--- Closes the currently active section (if any)
local function processItem(item, nowrapitems)
local function _closeCurrentSection()
if item:sub(1, 2) == '{|' then
if not activeSection then return end
-- 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.
local row = mw.html.create('tr'):addClass('navbox-section-row')
return '\n' .. item ..'\n'
local cell = mw.html.create('td'):attr('colspan',2)
if not hasrows then
_processImage(row,'imageleft')
end
end
if nowrapitems == 'yes' then
cell:node(sections[activeSection])
local lines = {}
row:node(cell)
for line in (item .. '\n'):gmatch('([^\n]*)\n') do
local prefix, content = line:match('^([*:;#]+)%s*(.*)')
local firstRow = false
if prefix and not content:match('^<span class="nowrap">') then
if not hasrows then
line = prefix .. '<span class="nowrap">' .. content .. '</span>'
firstRow = true
end
hasrows = true
table.insert(lines, line)
_processImage(row,'image')
end
item = table.concat(lines, '\n')
end
end
if item:match('^[*:;#]') then
_addGutter(navbox,not firstRow)
return '\n' .. item ..'\n'
navbox:node(row)
end
rowspan = rowspan + 1
return item
activeSection = false
hasData = false
end
end

--- Handles alternating rows
local function renderNavBar(titleCell)
--

-- @return Alternatingly returns true or false. Always returns false if alternating rows
if args.navbar ~= 'off' and args.navbar ~= 'plain' and not (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox') then
-- are disabled with "alternaterows = no"
titleCell:wikitext(navbar{
local function _alternateRow()
args.name,
if args.alternaterows == 'no' then return false end
mini = 1,
if alt then
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;'
alt = false
})
return true
else
alt = true
return false
end
end

end
end

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

local titleRow = tbl:tag('tr')
local subtable = mw.html.create('table'):addClass('navbox-section')

local headerrow = mw.html.create('tr')
if args.titlegroup then
local header = mw.html.create('th'):addClass('navbox-header'):attr('colspan',2):attr('scope','col'):wikitext( args['header'..num] )
titleRow
:tag('th')
local collapseme = args['state'..num] or false
:attr('scope', 'row')
local state = false
:addClass('navbox-group')
:addClass(args.titlegroupclass)
if collapseme then
:cssText(args.basestyle)
-- Look at this one
:cssText(args.groupstyle)
if collapseme ~= 'plain' then
:cssText(args.titlegroupstyle)
state = collapseme == 'expanded' and 'expanded' or 'collapsed'
:wikitext(args.titlegroup)
end
else
-- Look at default
local collapseall = args.defaultstate or false
if collapseall then
state = collapseall == 'expanded' and 'expanded' or 'collapsed'
end
end
end

if state then
local titleCell = titleRow:tag('th'):attr('scope', 'col')
subtable:addClass('mw-collapsible'):attr('data-expandtext',showText):attr('data-collapsetext',hideText)

if args.titlegroup then
if state == 'collapsed' then
subtable:addClass('mw-collapsed')
titleCell
end
:css('border-left', '2px solid #fdfdfd')
header:addClass('navbox-header-collapsible')
:css('width', '100%')
end
end

if args.headerclass then headerrow:addClass( args.headerclass ) end
local titleColspan = 2
if args.imageleft then titleColspan = titleColspan + 1 end
if args.headerstyle then header:cssText( args.headerstyle ) end
if args.image then titleColspan = titleColspan + 1 end
headerrow:node(header)
if args.titlegroup then titleColspan = titleColspan - 1 end
subtable:node(headerrow)

titleCell
sections[num] = subtable
:cssText(args.basestyle)
activeSection = num
:cssText(args.titlestyle)
:addClass('navbox-title')
:attr('colspan', titleColspan)

renderNavBar(titleCell)

titleCell
:tag('div')
:attr('id', mw.uri.anchorEncode(args.title))
:addClass(args.titleclass)
:css('font-size', '114%')
:css('margin', '0 4em')
:wikitext(processItem(args.title))
end
end

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

local function getAboveBelowColspan()
local row = mw.html.create('tr'):addClass('navbox-row')
local ret = 2
if args.imageleft then ret = ret + 1 end
if not hasrows and not activeSection then
if args.image then ret = ret + 1 end
_processImage(row, 'imageleft')
return ret
end

local function renderAboveRow(tbl)
if not args.above then return end

tbl:tag('tr')
:tag('td')
:addClass('navbox-abovebelow')
:addClass(args.aboveclass)
:cssText(args.basestyle)
:cssText(args.abovestyle)
:attr('colspan', getAboveBelowColspan())
:tag('div')
:wikitext(processItem(args.above, args.nowrapitems))
end

local function renderBelowRow(tbl)
if not args.below then return end

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

--
-- List rows
--
local function renderListRow(tbl, index, listnum)
local row = tbl:tag('tr')

if index == 1 and args.imageleft then
row
:tag('td')
: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

local listcell = mw.html.create('td'):addClass('navbox-list')
if args['group' .. listnum] then
local groupCell = row:tag('th')
local hlistcell = listcell:tag('div'):addClass('hlist')

local data = args['list'..num]
groupCell
:attr('scope', 'row')
if data:sub(1,1) == '*' then
:addClass('navbox-group')
-- Add newlines to support lists properly
:addClass(args.groupclass)
hlistcell
:cssText(args.basestyle)
:newline()
:css('width', args.groupwidth or '1%') -- If groupwidth not specified, minimize width
:wikitext( data )

:newline()
groupCell
else
:cssText(args.groupstyle)
hlistcell:wikitext( data )
:cssText(args['group' .. listnum .. 'style'])
:wikitext(args['group' .. listnum])
end
end

local listCell = row:tag('td')
local altRow = _alternateRow()
if altRow then

row:addClass( args.altrowclass or 'alt' )
if args['group' .. listnum] then
listCell
local listclass = args.altlistclass or args.listclass or false
:css('text-align', 'left')
if listclass then listcell:addClass( listclass ) end
:css('border-left-width', '2px')
:css('border-left-style', 'solid')
local liststyle = args.altliststyle or args.liststyle or false
if liststyle then listcell:cssText( liststyle ) end
else
else
if args.rowclass then row:addClass( args.rowclass ) end
listCell:attr('colspan', 2)
if args.listclass then listcell:addClass( args.listclass ) end
if args.liststyle then listcell:cssText( args.liststyle ) end
end
end

if not args.groupwidth then
if args['group'..num] then
local groupcell = mw.html.create('th'):addClass('navbox-group'):attr('scope','row'):wikitext( args['group'..num] )
listCell:css('width', '100%')
end
if altRow then

local groupclass = args.altgroupclass or args.groupclass or false
local rowstyle -- usually nil so cssText(rowstyle) usually adds nothing
if groupclass then groupcell:addClass( groupclass ) end
if index % 2 == 1 then
rowstyle = args.oddstyle
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
else
listcell:attr('colspan',2):addClass('no-group')
rowstyle = args.evenstyle
end
end

row:node( listcell )
local listText = args['list' .. listnum]
local oddEven = ODD_EVEN_MARKER
local firstRow = false
if listText:sub(1, 12) == '</div><table' then
if not hasrows and not activeSection then
-- Assume list text is for a subgroup navbox so no automatic striping for this row.
firstRow = true
oddEven = listText:find('<th[^>]*"navbox%-title"') and RESTART_MARKER or 'odd'
hasrows = true
_processImage(row, 'image')
end
end
listCell
if activeSection then
:css('padding', '0px')
local parent = sections[activeSection]
:cssText(args.liststyle)
if not isChild or not firstRow then
:cssText(rowstyle)
_addGutter(parent)
:cssText(args['list' .. listnum .. 'style'])
:addClass('navbox-list')
:addClass('navbox-' .. oddEven)
:addClass(args.listclass)
: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('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


--
-- Tracking categories
--

local function needsHorizontalLists()
if border == 'subgroup' or args.tracking == 'no' then
return false
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()
for _, key in ipairs({'titlestyle', 'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
if tostring(args[key]):find('background', 1, true) then
return true
end
end
parent:node(row)
end
hasData = true
end
else

if not isChild or not firstRow then
local function hasBorders()
_addGutter(navbox,not firstRow)
for _, key in ipairs({'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
if tostring(args[key]):find('border', 1, true) then
return true
end
end
navbox:node( row )
rowspan = rowspan + 1
end
end
end
end

--- Processes all rows
local function isIllegible()
local function processRows()
local styleratio = require('Module:Color contrast')._styleratio
sections = {}

for key, style in pairs(args) do
for i=1,#rownums do
local num = rownums[i]
if tostring(key):match("style$") then
if not skiprows[num] then
if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then
processHeader(num)
return true
processList(num)
end
end
end
end
end
_closeCurrentSection()
return false
end
if cimageleft then

cimageleft:attr('rowspan',rowspan)
local function getTrackingCategories()
end
local cats = {}
if cimage then
if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
cimage:attr('rowspan',rowspan)
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)
local title = mw.title.getCurrentTitle()
if title.namespace ~= 10 then return end -- not in template space
local subpage = title.subpageText
if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end

for _, cat in ipairs(getTrackingCategories()) do
builder:wikitext('[[Category:' .. cat .. ']]')
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.
--
--
-- @param frame The frame passed to the Module.
-- Main navbox tables
local function preProcessArgs(frame)
--
local function renderMainTable()
local tmp = {}
local tbl = mw.html.create('table')
if frame == mw.getCurrentFrame() then
:addClass('nowraplinks')
tmp = frame:getParent().args
:addClass(args.bodyclass)
else

tmp = frame
if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
tbl
:addClass('collapsible')
:addClass(args.state or 'autocollapse')
end
end

-- Storage tables
tbl:css('border-spacing', 0)
local nums = {}
if border == 'subgroup' or border == 'none' then
tbl
-- Loop over all the args
:addClass('navbox-subgroup')
for k,v in pairs(tmp) do
:cssText(args.bodystyle)
-- Skip empty args, which are useless
:cssText(args.style)
if v ~= '' then
else -- regular navbox - bodystyle and style will be applied to the wrapper table
local cat,num = tostring(k):match('^(%a+)([1-9]%d*)$')
tbl
:addClass('navbox-inner')
if cat == 'header' or cat == 'list' then
:css('background', 'transparent')
nums[num] = true
:css('color', 'inherit')
end
args[k] = v -- Simple copy
end
end
end
tbl:cssText(args.innerstyle)
colspan = args.image and 3 or 2

if args.imageleft then colspan = colspan + 1 end
renderTitleRow(tbl)
rowspan = 0
renderAboveRow(tbl)
for i, listnum in ipairs(listnums) do
if args.alternaterows == 'swap' then
renderListRow(tbl, i, listnum)
alt = true
end
end
renderBelowRow(tbl)


if not args.template and args.name ~= nil then
return tbl
args.template = args.name
end

function p._navbox(navboxArgs)
args = navboxArgs

for k, _ in pairs(args) do
if type(k) == 'string' then
local listnum = k:match('^list(%d+)$')
if listnum then table.insert(listnums, tonumber(listnum)) end
end
end
end
table.sort(listnums)
for k, v in pairs(nums) do

rownums[#rownums+1] = tonumber(k)
border = mw.text.trim(args.border or args[1] or '')
if border == 'child' then
border = 'subgroup'
end
end

table.sort(rownums)
-- render the main body of the navbox
local tbl = renderMainTable()
-- Calculate skip rows

local cSection, cSkip
-- render the appropriate wrapper around the navbox, depending on the border param
local res = mw.html.create()
local showall = args.showall
for i=1,#rownums do
if border == 'none' then
local nav = res:tag('div')
local num = rownums[i]
if args['header'..num] then
:attr('role', 'navigation')
cSection = true
:node(tbl)
cSkip = false
if args.title then
local showme = args['show'..num]
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title))
if showme == 'no' then
else
cSkip = true
nav:attr('aria-label', 'Navbox')
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
end
elseif border == 'subgroup' then
if cSection and cSkip then
skiprows[num] = true
-- 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
local nav = res:tag('div')
:attr('role', 'navigation')
:addClass('navbox')
:cssText(args.bodystyle)
:cssText(args.style)
:css('padding', '3px')
:node(tbl)
if args.title then
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title))
else
nav:attr('aria-label', 'Navbox')
end
end
end
end

renderTrackingCategories(res)

return striped(tostring(res))
end
end

------------------------------------------------
function p.navbox(frame)
-- MAIN FUNCTIONS
if not getArgs then
------------------------------------------------
getArgs = require('Module:Arguments').getArgs
--- 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
end
local border = args.border or trim(args[1]) or ''
args = getArgs(frame, {wrappers = {'Template:Navbox', 'Template:Navbox subgroup'}})
isChild = (border == 'child' or border == 'subgroup')
if frame.args.border then
-- This allows Template:Navbox_subgroup to use {{#invoke:Navbox|navbox|border=...}}.
if isChild then
args.border = frame.args.border
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',showText):attr('data-collapsetext',hideText)
if args.state == 'collapsed' then
navbox:addClass('mw-collapsed')
end
end
end
end

if args.bodyclass then navbox:addClass(args.bodyclass) end
-- Read the arguments in the order they'll be output in, to make references number in the right order.
if args.bodystyle then navbox:cssText(args.bodystyle) end
local _
_ = args.title
-- Process...
_ = args.above
if not isChild then
for i = 1, 20 do
processTitle()
_ = args["group" .. tostring(i)]
processAboveBelow('above')
_ = args["list" .. tostring(i)]
processRows()
processAboveBelow('below')
return tostring(navbox)
else
processRows()
local wrapper = mw.html.create('')
wrapper:wikitext('</div>')
wrapper:node(navbox)
wrapper:wikitext('<div>')
return tostring(wrapper)
end
end
_ = args.below

return p._navbox(args)
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
return p
-- [[Category:Modules]]

Revision as of 19:04, 19 February 2018

Documentation for this module may be created at Module:Navbox/doc

--------------------------------------------------------------------
-- Navbox Module
--
-- * Fully CSS styled (inline styles possible but not default)
-- * Supports unlimited rows
--
-- By User:Tjcool007 from layton.wikia.com
--------------------------------------------------------------------
 
local p = {}
 
local args = {} -- Arguments passed to template
local navbox -- Actual navbox

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'
 
------------------------------------------------
-- 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'
	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',showText):attr('data-collapsetext',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

	if not args.template and args.name ~= nil then
		args.template = args.name
	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',showText):attr('data-collapsetext',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>')
		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
-- [[Category:Modules]]