Module:Navbox: Difference between revisions

From PSP2i Wiki
(Created page with "-- -- This module implements {{Navbox}} -- local p = {} local navbar = require('Module:Navbar')._navbar local getArgs -- lazily initialized local args local border local li...")
 
No edit summary
 
Line 1: Line 1:
-- <nowiki>
--
--
-- This module implements {{Navbox}}
-- Implements {{navbox}}
--
--


local p = {}
local p = {}
local tnavbar = require( 'Module:Tnavbar' )
local yesno = require( 'Module:Yesno' )
local page_title = mw.title.getCurrentTitle().fullText
--
-- Helper for inserting a new row into the navbox
--
-- @param tbl {mw.html table}
-- @return tbl {mw.html table}
--
local function insertRow( tbl )
return tbl:tag( 'tr' )
end
--
-- Creates the navbox table
--
-- @param args {table}
-- @return tbl {mw.html table}
--
local function createTbl( args )
local tbl = mw.html.create( 'table' )


local navbar = require('Module:Navbar')._navbar
tbl
local getArgs -- lazily initialized
:addClass( yesno(args.subgroup, false) and 'navbox-subgroup' or 'navbox' )
:addClass( 'nowraplinks' )


local args
if not yesno(args.subgroup, false) and
local border
( args.state == 'collapsed' or
local listnums
  args.state == 'uncollapsed' or
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
  args.state == 'autocollapse' or
local RESTART_MARKER = '\127_ODDEVEN0_\127'
  -- defaults to autocollapse
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'
  args.state == nil )
then
tbl:addClass( 'mw-collapsible' )


local function striped(wikitext)
if args.state == 'collapsed' then
-- Return wikitext with markers replaced for odd/even striping.
tbl:addClass( 'mw-collapsed' )
-- Child (subgroup) navboxes are flagged with a category that is removed
elseif args.state == 'uncollapsed' then
-- by parent navboxes. The result is that the category shows all pages
tbl:addClass('navbox-uncollapsed')
-- where a child navbox is not contained in a parent navbox.
local orphanCat = '[[Category:Navbox orphans]]'
if border == 'subgroup' and args.orphan ~= 'yes' then
-- No change; striping occurs in outermost navbox.
return wikitext .. orphanCat
end
local first, second = 'odd', 'even'
if args.evenodd then
if args.evenodd == 'swap' then
first, second = second, first
else
first = args.evenodd
second = first
end
end
end
end
local changer
 
if first == second then
if yesno(args.collapsible, false) then
changer = first
tbl:addClass( 'navbox-collapsible' )
else
local index = 0
changer = function (code)
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
end
local regex = orphanCat:gsub('([%[%]])', '%%%1')
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer))  -- () omits gsub count
end


local function processItem(item, nowrapitems)
if args.style then
if item:sub(1, 2) == '{|' then
tbl:cssText( args.style )
-- 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.
return '\n' .. item ..'\n'
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
end
if item:match('^[*:;#]') then
return '\n' .. item ..'\n'
end
return item
end


local function renderNavBar(titleCell)
-- manually set collapse/expand messages
 
-- bug causing the default database messages to be used
if args.navbar ~= 'off' and args.navbar ~= 'plain' and not (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox') then
tbl
titleCell:wikitext(navbar{
:attr( {
args.name,
['data-expandtext'] = 'show',
mini = 1,
['data-collapsetext'] = 'hide',
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;box-shadow:none;padding:0;'
['data-navbox-name'] = args.name
})
} )
end


return tbl
end
end


--
--
--   Title row
-- Wrapper for [[Module:Tnavbar]]
--
-- @param args {table}
-- @return {string}
--
--
local function renderTitleRow(tbl)
local function navbar( args )
if not args.title then return end
return tnavbar._collapsible( { [1] = args.title, [2] = args.name } )
 
local titleRow = tbl:tag('tr')
 
if args.titlegroup then
titleRow
:tag('th')
:attr('scope', 'row')
:addClass('navbox-group')
:addClass(args.titlegroupclass)
:cssText(args.basestyle)
:cssText(args.groupstyle)
:cssText(args.titlegroupstyle)
:wikitext(args.titlegroup)
end
 
local titleCell = titleRow:tag('th'):attr('scope', 'col')
 
if args.titlegroup then
titleCell
:css('border-left', '2px solid #fdfdfd')
:css('width', '100%')
end
 
local titleColspan = 2
if args.imageleft then titleColspan = titleColspan + 1 end
if args.image then titleColspan = titleColspan + 1 end
if args.titlegroup then titleColspan = titleColspan - 1 end
 
titleCell
:cssText(args.basestyle)
:cssText(args.titlestyle)
:addClass('navbox-title')
:attr('colspan', titleColspan)
 
renderNavBar(titleCell)
 
titleCell
:tag('div')
-- 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


--
--
--   Above/Below rows
-- Creates the header (what you see when the navbox is collapsed)
--
--
-- @param tbl {mw.html table}
-- @param args {table}
-- @return {mw.html table}
--
local function header( tbl, args )
    local paddingClass
    if args.name then
        if args.state == 'plain' then
            paddingClass = 'navbox-title-name-plain'
        else
            paddingClass = 'navbox-title-name-notplain'
        end
    else
        if args.state == 'plain' then
            paddingClass = 'navbox-title-noname-plain'
        else
            paddingClass = 'navbox-title-noname-notplain'
        end
    end
   
    local div = insertRow( tbl )
        :tag( 'th' )
            :attr( 'colspan', '2' )
            :addClass( 'navbox-title' )
            :attr( 'id' , 'navbox-title' )
                :tag( 'div' )
                    :addClass ( paddingClass  )
                    :wikitext( args.name and navbar( args ) or args.title )


local function getAboveBelowColspan()
    return div:allDone()
local ret = 2
if args.imageleft then ret = ret + 1 end
if args.image then ret = ret + 1 end
return ret
end
end


local function renderAboveRow(tbl)
--
if not args.above then return end
-- Inserts a row into the navbox
--
-- @param tbl {mw.html table}
-- @param gtitle {string}
-- @param group {string}
-- @param gtype {string}
-- @param style {string}
-- @return {mw.html table}
--
local function row( tbl, gtitle, group, gtype, style, _name, subgroup )
local tr = insertRow( tbl )
local td


tbl:tag('tr')
if gtitle then
:tag('td')
td = tr
:addClass('navbox-abovebelow')
:addClass( 'navbox-group' )
:addClass(args.aboveclass)
:tag( 'td' )
:cssText(args.basestyle)
:addClass( 'navbox-group-title' )
:cssText(args.abovestyle)
:wikitext( gtitle )
:attr('colspan', getAboveBelowColspan())
:done()
:tag('div')
:tag( 'td' )
-- id for aria-labelledby attribute, if no title
else
:attr('id', args.title and nil or mw.uri.anchorEncode(args.above))
td = tr
:wikitext(processItem(args.above, args.nowrapitems))
:addClass( 'navbox-group' )
end
:addClass( 'navbox-group-split' )
:tag( 'td' )
:addClass( 'navbox-group-title-hidden' )
:attr( 'colspan', '0' )
:css( 'display', 'none' )
:done()
:tag( 'td' )
:attr( 'colspan', '2' )
end


local function renderBelowRow(tbl)
--[[
if not args.below then return end
  List styling
  This is unlikely to be implemented in the near future due to it requiring extra css to work
  and mobile currently not supporting that css.
  As an example, it lets you do the following instead if using {{*}} all the time
  | group3 =
  * {{plink|foo}}
  * {{plink|bar}}
  * {{plink|baz}}
]]
if mw.ustring.match( group, '^%s*%*' ) then
td:newline()


tbl:tag('tr')
-- trim whitespace on bullets
:tag('td')
local spl = mw.text.split( group, '\n' )
:addClass('navbox-abovebelow')
:addClass(args.belowclass)
:cssText(args.basestyle)
:cssText(args.belowstyle)
:attr('colspan', getAboveBelowColspan())
:tag('div')
:wikitext(processItem(args.below, args.nowrapitems))
end


--
for i = 1, #spl do
--  List rows
spl[i] = mw.text.trim( spl[i] )
--
end
local function renderListRow(tbl, index, listnum)
local row = tbl:tag('tr')


if index == 1 and args.imageleft then
group = '\n' .. table.concat( spl, '\n' )
row
: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 group2 = group
local groupCell = row:tag('th')
--local group3 = group2
-- analytics


-- id for aria-labelledby attribute, if lone group with no title or above
--if _name then
if listnum == 1 and not (args.title or args.above or args.group2) then
-- local name = mw.ustring.gsub(_name,' ','_')
groupCell
-- for v in mw.ustring.gmatch(group,'%[%[[^%]]+%]%]') do
:attr('id', mw.uri.anchorEncode(args.group1))
-- if mw.ustring.match(v,'%[%[File:.+|link=') then
end
-- local link = mw.ustring.match(v,'|link=([^%]|]+)')
-- if link then
-- local linkrep = mw.ustring.gsub(link,'([%%%]%[%-^$*()+?])','%%%1')
-- local _link = mw.ustring.gsub(link,' ','_')
-- local newfile = mw.ustring.gsub(v,'|link='..linkrep,string.format('|link=https://oldschool.runescape.wiki/w/%s?f=%s',_link,name))
-- local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
-- group2 = mw.ustring.gsub(group2,w,newfile)
-- end
-- elseif mw.ustring.match(v,'%[%[Category:') then
-- nothing
-- else
-- local link = mw.ustring.match(v,'%[%[([^%]|]+)')
-- local txt = mw.ustring.match(v,'%|([^%]|]+)') or link
 
-- local newlink = ''


groupCell
-- black links if current page
:attr('scope', 'row')
-- if link == page_title then
:addClass('navbox-group')
-- newlink = string.format('<b>%s</b>',txt)
:addClass(args.groupclass)
-- else
:cssText(args.basestyle)
-- local _link = mw.ustring.gsub(link or '',' ','_')
:css('width', args.groupwidth or '1%') -- If groupwidth not specified, minimize width
-- newlink = string.format('[https://oldschool.runescape.wiki.com/w/%s?n=%s %s]',_link,name,txt)
-- end
-- local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
-- group2 = mw.ustring.gsub(group2,w,newlink)
-- end
-- end


groupCell
--[==[
:cssText(args.groupstyle)
fix [[these kind]]s of [[link]]s post analytics parse
:cssText(args['group' .. listnum .. 'style'])
]==]
:wikitext(args['group' .. listnum])
-- group3 = group2
end


local listCell = row:tag('td')
-- for v in mw.ustring.gmatch(group2,'%[https://oldschool.runescape.wiki.com/w[^%]]-%]%a') do
-- local rep = mw.ustring.gsub(v,'%]','')
-- rep = rep..']'
-- local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')


if args['group' .. listnum] then
-- group3 = mw.ustring.gsub(group2,w,rep)
listCell
-- end
:css('text-align', 'left')
--end
:css('border-left-width', '2px')
:css('border-left-style', 'solid')
else
listCell:attr('colspan', 2)
end


if not args.groupwidth then
td
listCell:css('width', '100%')
:addClass( 'navbox-list' )
end
:wikitext( group ) --group3


local rowstyle  -- usually nil so cssText(rowstyle) usually adds nothing
if gtype and mw.ustring.lower( gtype ) == 'subgroup' then
if index % 2 == 1 then
td
rowstyle = args.oddstyle
:addClass( 'navbox-parent' )
else
:css( {
rowstyle = args.evenstyle
padding = '0'
} )
end
end


local listText = args['list' .. listnum]
if style then
local oddEven = ODD_EVEN_MARKER
td:cssText( style )
if listText:sub(1, 12) == '</div><table' then
-- Assume list text is for a subgroup navbox so no automatic striping for this row.
oddEven = listText:find('<th[^>]*"navbox%-title"') and RESTART_MARKER or 'odd'
end
end
listCell
:css('padding', '0px')
:cssText(args.liststyle)
:cssText(rowstyle)
:cssText(args['list' .. listnum .. 'style'])
:addClass('navbox-list')
:addClass('navbox-' .. oddEven)
: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
return td:allDone()
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


--
--
--   Tracking categories
-- Inserts a footer into the navbox
--
-- @param tbl {mw.html table}
-- @param args {table}
-- @return {mw.html table}
--
--
local function footer( tbl, args )
local th = insertRow( tbl )
:tag( 'th' )
:attr( 'colspan', '2' )
:addClass( 'navbox-footer' )


local function needsHorizontalLists()
if args.fstyle then
if border == 'subgroup' or args.tracking == 'no' then
th:cssText( args.fstyle )
return false
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()
if mw.ustring.match( args.footer, '^%s*%*' ) then
for _, key in ipairs({'titlestyle', 'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
th:newline()
if tostring(args[key]):find('background', 1, true) then
 
return true
-- trim whitespace on bullets
end
local spl = mw.text.split( args.footer, '\n' )
end
end


local function hasBorders()
for i = 1, #spl do
for _, key in ipairs({'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
spl[i] = mw.text.trim( spl[i] )
if tostring(args[key]):find('border', 1, true) then
return true
end
end
end
end


local function isIllegible()
args.footer = table.concat( spl, '\n' )
local styleratio = require('Module:Color contrast')._styleratio


for key, style in pairs(args) do
th:addClass( 'navbox-list' )
if tostring(key):match("style$") then
if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then
return true
end
end
end
end
return false
end


local function getTrackingCategories()
th:wikitext( args.footer )
local cats = {}
 
if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
return th:allDone()
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
end


local function renderTrackingCategories(builder)
--
-- Adds [[Category:Navbox templates]] to navbox template pages
--
-- @return {string}
--
local function categories()
local title = mw.title.getCurrentTitle()
local title = mw.title.getCurrentTitle()
if title.namespace ~= 10 then return end -- not in template space
local page = title.text
local subpage = title.subpageText
local ns = title.nsText
if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end


for _, cat in ipairs(getTrackingCategories()) do
if ns == 'Template' then
builder:wikitext('[[Category:' .. cat .. ']]')
-- sort in category by pagename
return '[[Category:Navbox templates| ' .. page .. ']]'
else
return ''
end
end
end
end


--
--
--   Main navbox tables
-- Adds [[Template:Navbox/doc]] to navbox template pages
--
-- @param args {table}
-- @return {string}
--
--
local function renderMainTable()
local function docs( args )
local tbl = mw.html.create('table')
local frame = mw.getCurrentFrame()
:addClass('nowraplinks')
local title = mw.title.getCurrentTitle()
:addClass(args.bodyclass)
local base = title.baseText
local ns = title.nsText


if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
-- not if a subpage of [[Template:Navbox]]
if args.state == 'collapsed' then args.state = 'mw-collapsed' end
if base ~= 'Navbox' and
tbl
-- in template ns
:addClass('mw-collapsible')
ns == 'Template' and
:addClass(args.state or 'autocollapse')
-- not a navbox group within a navbox
end
not yesno(args.subgroup, false) and
 
-- not a collapsible navbox within a navbox
tbl:css('border-spacing', 0)
not yesno(args.collapsible, false) and
if border == 'subgroup' or border == 'none' then
-- not if the doc argument is not set to "yes"
tbl
yesno(args.doc, false)
:addClass('navbox-subgroup')
then
:cssText(args.bodystyle)
return frame:expandTemplate{ title = 'Navbox/doc' }
:cssText(args.style)
else
else  -- regular navbox - bodystyle and style will be applied to the wrapper table
return ''
tbl
:addClass('navbox-inner')
:css('background', 'transparent')
:css('color', 'inherit')
end
tbl:cssText(args.innerstyle)
 
renderTitleRow(tbl)
renderAboveRow(tbl)
for i, listnum in ipairs(listnums) do
renderListRow(tbl, i, listnum)
end
end
renderBelowRow(tbl)


return tbl
end
end


function p._navbox(navboxArgs)
--
args = navboxArgs
-- Navbox method to allow it to be called by other modules
listnums = {}
--
 
-- @param _args {table}
for k, _ in pairs(args) do
-- @return {string}
if type(k) == 'string' then
--
local listnum = k:match('^list(%d+)$')
function p._navbox( _args )
if listnum then table.insert(listnums, tonumber(listnum)) end
local args = {}
local wkCss = ''
local wkDiv = ''
local j
-- preserves parser function behaviour where an empty string is considered undefined
-- or nil in lua's case
for k, v in pairs( _args ) do
if v ~= '' then
args[k] = v
end
end
end
end
table.sort(listnums)


border = mw.text.trim(args.border or args[1] or '')
local tbl = createTbl( args )
if border == 'child' then
 
border = 'subgroup'
if not yesno(args.subgroup, false) then
tbl = header( tbl, args )
end
end


-- render the main body of the navbox
-- insert up to 25 rows
local tbl = renderMainTable()
for i = 1, 25 do
j = tostring( i )


-- render the appropriate wrapper around the navbox, depending on the border param
if args['group' .. j] then
local res = mw.html.create()
tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], args['style' .. j], args.name, args.subgroup )
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
local nav = res:tag('div')
: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
else
nav:attr('aria-label', 'Navbox')
break
end
end
end
end


if (args.nocat or 'false'):lower() == 'false' then
if args.footer then
renderTrackingCategories(res)
tbl = footer( tbl, args )
end
end
return striped(tostring(res))
end


function p.navbox(frame)
tbl = tostring( tbl )
if not getArgs then
 
getArgs = require('Module:Arguments').getArgs
local cats = ''
if not yesno(args.subgroup, false) and not yesno(args.hidecat, false) then
cats = categories()
end
end
args = getArgs(frame, {wrappers = {'Template:Navbox'}})
local docs = docs( args )


-- Read the arguments in the order they'll be output in, to make references number in the right order.
return tbl .. cats .. docs
local _
end
_ = args.title
_ = args.above
for i = 1, 20 do
_ = args["group" .. tostring(i)]
_ = args["list" .. tostring(i)]
end
_ = args.below


return p._navbox(args)
--
-- Main navbox method accessed through #invoke
--
-- @param frame {table}
-- @return {string}
--
function p.navbox( frame )
local args = frame:getParent().args
return p._navbox( args )
end
end


return p
return p
-- </nowiki>

Latest revision as of 21:45, 7 November 2021


-- <nowiki>
--
-- Implements {{navbox}}
--

local p = {}
local tnavbar = require( 'Module:Tnavbar' )
local yesno = require( 'Module:Yesno' )
local page_title = mw.title.getCurrentTitle().fullText
--
-- Helper for inserting a new row into the navbox
--
-- @param tbl {mw.html table}
-- @return tbl {mw.html table}
--
local function insertRow( tbl )
	return tbl:tag( 'tr' )
end

--
-- Creates the navbox table
--
-- @param args {table}
-- @return tbl {mw.html table}
--
local function createTbl( args )

	local tbl = mw.html.create( 'table' )

	tbl
		:addClass( yesno(args.subgroup, false) and 'navbox-subgroup' or 'navbox' )
		:addClass( 'nowraplinks' )

	if not yesno(args.subgroup, false) and
		( args.state == 'collapsed' or
		  args.state == 'uncollapsed' or
		  args.state == 'autocollapse' or
		  -- defaults to autocollapse
		  args.state == nil )
	then
		tbl:addClass( 'mw-collapsible' )

		if args.state == 'collapsed' then
			tbl:addClass( 'mw-collapsed' )
		elseif args.state == 'uncollapsed' then
			tbl:addClass('navbox-uncollapsed')
		end
	end

	if yesno(args.collapsible, false) then
		tbl:addClass( 'navbox-collapsible' )
	end

	if args.style then
		tbl:cssText( args.style )
	end

	-- manually set collapse/expand messages
	-- bug causing the default database messages to be used
	tbl
		:attr( {
			['data-expandtext'] = 'show',
			['data-collapsetext'] = 'hide',
			['data-navbox-name'] = args.name
		} )

	return tbl
end

--
-- Wrapper for [[Module:Tnavbar]]
--
-- @param args {table}
-- @return {string}
--
local function navbar( args )
	return tnavbar._collapsible( { [1] = args.title, [2] = args.name } )
end

--
-- Creates the header (what you see when the navbox is collapsed)
--
-- @param tbl {mw.html table}
-- @param args {table}
-- @return {mw.html table}
--
local function header( tbl, args )
    local paddingClass
    if args.name then
        if args.state == 'plain' then
            paddingClass = 'navbox-title-name-plain'
        else
            paddingClass = 'navbox-title-name-notplain'
        end
    else
        if args.state == 'plain' then
            paddingClass = 'navbox-title-noname-plain'
        else
            paddingClass = 'navbox-title-noname-notplain'
        end
    end
    
    local div = insertRow( tbl )
        :tag( 'th' )
            :attr( 'colspan', '2' )
            :addClass( 'navbox-title' )
            :attr( 'id' , 'navbox-title' )
                :tag( 'div' )
                    :addClass ( paddingClass  )
                    :wikitext( args.name and navbar( args ) or args.title )

    return div:allDone()
end

--
-- Inserts a row into the navbox
--
-- @param tbl {mw.html table}
-- @param gtitle {string}
-- @param group {string}
-- @param gtype {string}
-- @param style {string}
-- @return {mw.html table}
--
local function row( tbl, gtitle, group, gtype, style, _name, subgroup )
	local tr = insertRow( tbl )
	local td

	if gtitle then
		td = tr
			:addClass( 'navbox-group' )
			:tag( 'td' )
				:addClass( 'navbox-group-title' )
				:wikitext( gtitle )
				:done()
			:tag( 'td' )
	else
		td = tr
			:addClass( 'navbox-group' )
			:addClass( 'navbox-group-split' )
			:tag( 'td' )
				:addClass( 'navbox-group-title-hidden' )
				:attr( 'colspan', '0' )
				:css( 'display', 'none' )
				:done()
			:tag( 'td' )
				:attr( 'colspan', '2' )
	end

	--[[
	   List styling
	   This is unlikely to be implemented in the near future due to it requiring extra css to work
	   and mobile currently not supporting that css.
	   As an example, it lets you do the following instead if using {{*}} all the time
	   | group3 =
	   * {{plink|foo}}
	   * {{plink|bar}}
	   * {{plink|baz}}
	]]
	if mw.ustring.match( group, '^%s*%*' ) then
		td:newline()

		-- trim whitespace on bullets
		local spl = mw.text.split( group, '\n' )

		for i = 1, #spl do
			spl[i] = mw.text.trim( spl[i] )
		end

		group = '\n' .. table.concat( spl, '\n' )		
	end

	--local group2 = group
	--local group3 = group2
	-- analytics

	--if _name then
	--	local name = mw.ustring.gsub(_name,' ','_')
	--	for v in mw.ustring.gmatch(group,'%[%[[^%]]+%]%]') do
	--		if mw.ustring.match(v,'%[%[File:.+|link=') then
	--			local link = mw.ustring.match(v,'|link=([^%]|]+)')
	--			if link then
	--				local linkrep = mw.ustring.gsub(link,'([%%%]%[%-^$*()+?])','%%%1')
	--				local _link = mw.ustring.gsub(link,' ','_')
	--				local newfile = mw.ustring.gsub(v,'|link='..linkrep,string.format('|link=https://oldschool.runescape.wiki/w/%s?f=%s',_link,name))
	--				local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
	--				group2 = mw.ustring.gsub(group2,w,newfile)
	--			end
	--		elseif mw.ustring.match(v,'%[%[Category:') then
				-- nothing
	--		else
	--			local link = mw.ustring.match(v,'%[%[([^%]|]+)')
	--			local txt = mw.ustring.match(v,'%|([^%]|]+)') or link

	--			local newlink = ''

				-- black links if current page
	--			if link == page_title then
	--				newlink = string.format('<b>%s</b>',txt)
	--			else
	--				local _link = mw.ustring.gsub(link or '',' ','_')
	--				newlink = string.format('[https://oldschool.runescape.wiki.com/w/%s?n=%s %s]',_link,name,txt)
	--			end
	--			local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
	--			group2 = mw.ustring.gsub(group2,w,newlink)
	--		end
	--	end

		--[==[
			fix [[these kind]]s of [[link]]s post analytics parse
			]==]
	--	group3 = group2

	--	for v in mw.ustring.gmatch(group2,'%[https://oldschool.runescape.wiki.com/w[^%]]-%]%a') do
	--		local rep = mw.ustring.gsub(v,'%]','')
	--		rep = rep..']'
	--		local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')

	--		group3 = mw.ustring.gsub(group2,w,rep)
	--	end
	--end

	td
		:addClass( 'navbox-list' )
		:wikitext( group ) --group3

	if gtype and mw.ustring.lower( gtype ) == 'subgroup' then
		td
			:addClass( 'navbox-parent' )
			:css( {
				padding = '0'
			} )
	end

	if style then
		td:cssText( style )
	end

	return td:allDone()
end

--
-- Inserts a footer into the navbox
--
-- @param tbl {mw.html table}
-- @param args {table}
-- @return {mw.html table}
--
local function footer( tbl, args )
	local th = insertRow( tbl )
		:tag( 'th' )
			:attr( 'colspan', '2' )
			:addClass( 'navbox-footer' )

	if args.fstyle then
		th:cssText( args.fstyle )
	end

	if mw.ustring.match( args.footer, '^%s*%*' ) then
		th:newline()

		-- trim whitespace on bullets
		local spl = mw.text.split( args.footer, '\n' )

		for i = 1, #spl do
			spl[i] = mw.text.trim( spl[i] )
		end

		args.footer = table.concat( spl, '\n' )

		th:addClass( 'navbox-list' )
	end

	th:wikitext( args.footer )

	return th:allDone()
end

--
-- Adds [[Category:Navbox templates]] to navbox template pages
--
-- @return {string}
--
local function categories()
	local title = mw.title.getCurrentTitle()
	local page = title.text
	local ns = title.nsText

	if ns == 'Template' then
		-- sort in category by pagename
		return '[[Category:Navbox templates| ' .. page .. ']]'
	else
		return ''
	end

end

--
-- Adds [[Template:Navbox/doc]] to navbox template pages
--
-- @param args {table}
-- @return {string}
--
local function docs( args )
	local frame = mw.getCurrentFrame()
	local title = mw.title.getCurrentTitle()
	local base = title.baseText
	local ns = title.nsText

		-- not if a subpage of [[Template:Navbox]]
	if base ~= 'Navbox' and
		-- in template ns
		ns == 'Template' and
		-- not a navbox group within a navbox
		not yesno(args.subgroup, false) and
		-- not a collapsible navbox within a navbox
		not yesno(args.collapsible, false) and
		-- not if the doc argument is not set to "yes"
		yesno(args.doc, false)
	then
		return frame:expandTemplate{ title = 'Navbox/doc' }
	else
		return ''
	end

end

--
-- Navbox method to allow it to be called by other modules
--
-- @param _args {table}
-- @return {string}
--
function p._navbox( _args )
	local args = {}
	local wkCss = ''
	local wkDiv = ''
	local j
	
	-- preserves parser function behaviour where an empty string is considered undefined
	-- or nil in lua's case
	for k, v in pairs( _args ) do
		if v ~= '' then
			args[k] = v
		end
	end

	local tbl = createTbl( args )

	if not yesno(args.subgroup, false) then
		tbl = header( tbl, args )
	end

	-- insert up to 25 rows
	for i = 1, 25 do
		j = tostring( i )

		if args['group' .. j] then
			tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], args['style' .. j], args.name, args.subgroup )
		else
			break
		end
	end

	if args.footer then
		tbl = footer( tbl, args )
	end

	tbl = tostring( tbl )

	local cats = ''
	if not yesno(args.subgroup, false) and not yesno(args.hidecat, false) then
		cats = categories()
	end
	local docs = docs( args )

	return tbl .. cats .. docs
end

--
-- Main navbox method accessed through #invoke
--
-- @param frame {table}
-- @return {string}
--
function p.navbox( frame )
	local args = frame:getParent().args
	return p._navbox( args )
end

return p
-- </nowiki>