Module:Navbox: Difference between revisions

From WIDEVERSE Wiki
Jump to navigation Jump to search
m (1 revision imported)
 
(Undo imported revision 14948 by user en>Uzume)
Tag: Undo
 
(18 intermediate revisions by 6 users not shown)
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 onmain = require('Module:Mainonly').on_main()
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


local navbar = require('Module:Navbar')._navbar
--
local getArgs -- lazily initialized
-- Creates the navbox table
--
-- @param args {table}
-- @return tbl {mw.html table}
--
local function createTbl( args )


local args
local tbl = mw.html.create( 'table' )
local border
local listnums = {}
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
local RESTART_MARKER = '\127_ODDEVEN0_\127'
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'


local function striped(wikitext)
tbl
-- Return wikitext with markers replaced for odd/even striping.
:addClass( yesno( args.subgroup ) and 'navbox-subgroup' or 'navbox' )
-- Child (subgroup) navboxes are flagged with a category that is removed
:addClass( 'nowraplinks' )
-- by parent navboxes. The result is that the category shows all pages
 
-- where a child navbox is not contained in a parent navbox.
if not yesno( args.subgroup ) and
local orphanCat = '[[Category:Navbox orphans]]'
( args.state == 'collapsed' or
if border == 'subgroup' and args.orphan ~= 'yes' then
  args.state == 'uncollapsed' or
-- No change; striping occurs in outermost navbox.
  args.state == 'autocollapse' or
return wikitext .. orphanCat
  -- defaults to autocollapse
end
  args.state == nil )
local first, second = 'odd', 'even'
then
if args.evenodd then
tbl:addClass( 'mw-collapsible' )
if args.evenodd == 'swap' then
 
first, second = second, first
if args.state == 'collapsed' then
else
tbl:addClass( 'mw-collapsed' )
first = args.evenodd
elseif args.state == 'uncollapsed' then
second = first
tbl:addClass('navbox-uncollapsed')
end
end
end
end
local changer
 
if first == second then
if yesno( args.collapsible ) 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
if item:match('^[*:;#]') then
return '\n' .. item ..'\n'
end
end
return item
end


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


if args.navbar ~= 'off' and args.navbar ~= 'plain' and not (not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox') then
return tbl
titleCell:wikitext(navbar{
end
args.name,
mini = 1,
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none;'
})
end


--
-- Wrapper for [[Module:Tnavbar]]
--
-- @param args {table}
-- @return {string}
--
local function navbar( args )
return tnavbar._collapsible( { [1] = '<span style="display:none;">RS3 </span>'..args.title, [2] = args.name } )
end
end


--
--
--   Title row
-- 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 renderTitleRow(tbl)
local function header( tbl, args )
if not args.title then return end
local div = insertRow( tbl )
:tag( 'th' )
:attr( 'colspan', '2' )
:addClass( 'navbox-title' )
:attr( 'id' , 'navbox-title' )
:tag( 'div' )


local titleRow = tbl:tag('tr')
-- @todo move this to site css so we can simplify this (hook off a class)
 
-- to something like div:wikitext( args.name and navbar( args ) or args.title )
if args.titlegroup then
-- which can be appended to the above and returned straight away
titleRow
if args.name then
:tag('th')
div
:attr('scope', 'row')
:css( 'padding-right', args.state == 'plain' and '6em' or '0' )
:addClass('navbox-group')
:wikitext( navbar( args ) )
:addClass(args.titlegroupclass)
else
:cssText(args.basestyle)
div
:cssText(args.groupstyle)
:css( 'padding-left', args.state == 'plain' and '0' or '6em' )
:cssText(args.titlegroupstyle)
:wikitext( '<span style="display:none;">RS3 </span>'..args.title )
: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
end


local titleColspan = 2
return div:allDone()
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')
: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
-- Inserts a row into the navbox
--
-- @param tbl {mw.html table}
-- @param gtitle {string}
-- @param group {string}
-- @param gtype {string}
-- @param gcats {table}
-- @param style {string}
-- @return {mw.html table}
--
--
local function row( tbl, gtitle, group, gtype, gcats, 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


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


local function renderAboveRow(tbl)
-- trim whitespace on bullets
if not args.above then return end
local spl = mw.text.split( group, '\n' )


tbl:tag('tr')
for i = 1, #spl do
:tag('td')
spl[i] = mw.text.trim( spl[i] )
:addClass('navbox-abovebelow')
end
: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)
group = '\n' .. table.concat( spl, '\n' )
if not args.below then return end
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


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


if index == 1 and args.imageleft then
--if _name then
row
-- local name = mw.ustring.gsub(_name,' ','_')
:tag('td')
-- for v in mw.ustring.gmatch(group,'%[%[[^%]]+%]%]') do
:addClass('navbox-image')
-- if mw.ustring.match(v,'%[%[File:.+|link=') then
:addClass(args.imageclass)
-- local link = mw.ustring.match(v,'|link=([^%]|]+)')
:css('width', '1px')               -- Minimize width
-- if link then
:css('padding', '0px 2px 0px 0px')
-- local linkrep = mw.ustring.gsub(link,'([%%%]%[%-^$*()+?])','%%%1')
:cssText(args.imageleftstyle)
-- local _link = mw.ustring.gsub(link,' ','_')
:attr('rowspan', #listnums)
-- local newfile = mw.ustring.gsub(v,'|link='..linkrep,string.format('|link=https://runescape.wiki/w/%s?f=%s',_link,name))
:tag('div')
-- local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
:wikitext(processItem(args.imageleft))
-- group2 = mw.ustring.gsub(group2,w,newfile)
end
-- end
-- elseif mw.ustring.match(v,'%[%[Category:') then
-- nothing
-- else
-- local link = mw.ustring.match(v,'%[%[([^%]|]+)')
-- local txt = mw.ustring.match(v,'%|([^%]|]+)') or link


if args['group' .. listnum] then
-- local newlink = ''
local groupCell = row:tag('th')


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://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://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',
['border-bottom'] = '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')
-- add subgroup categories
:cssText(args.liststyle)
if next(gcats) and onmain then
:cssText(rowstyle)
first_char = page_title:sub(1,1)
:cssText(args['list' .. listnum .. 'style'])
title_pattern = "[" .. first_char:upper() .. first_char:lower() .. "]" .. page_title:sub(2)
:addClass('navbox-list')
title_pattern = title_pattern :gsub("%(","%%(")
:addClass('navbox-' .. oddEven)
:gsub("%)","%%)")
:addClass(args.listclass)
:gsub("_"," ")
:tag('div')
:gsub(" ","[_ ]")
:css('padding', (index == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
:gsub("%-","%%-")
:wikitext(processItem(listText, args.nowrapitems))
local link_patterns = {
 
"%[%["..title_pattern.."%]%]", "%[%["..title_pattern.."%|", "{{[Pp]link%|"..title_pattern.."}}",
if index == 1 and args.image then
"{{[Pp]link%|"..title_pattern.."%|", "{{[Pp]linkp%|"..title_pattern.."}}",
row
"{{[Pp]linkp%|"..title_pattern.."%|", "{{[Ii]linkp%|"..title_pattern.."}}",
:tag('td')
"{{[Ii]linkp%|"..title_pattern.."%|", "{{[Cc]hatl%|"..title_pattern.."}}",
:addClass('navbox-image')
"{{[Cc]hatl%|"..title_pattern.."%|"
:addClass(args.imageclass)
}
:css('width', '1px')              -- Minimize width
for _,v in ipairs(link_patterns) do
:css('padding', '0px 0px 0px 2px')
if group:match(v) then
:cssText(args.imagestyle)
for _,cat in ipairs(gcats) do
:attr('rowspan', #listnums)
td:wikitext('[[Category:'..cat..']]')
:tag('div')
end
:wikitext(processItem(args.image))
break
end
end
end
end
return td:allDone()
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'}) do
th:newline()
if tostring(args[key]):find('background', 1, true) then
 
return true
-- 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
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
return cats
end
end


local function renderTrackingCategories(builder)
--
-- Adds [[Category:Navigational 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:Navigational templates|' .. page .. ']]'
else
return ''
end
end
end
end


--
--
--   Main navbox tables
-- Adds [[Template:Navbox/doc]] to navbox template pages
--
--
local function renderMainTable()
-- @param args {table}
local tbl = mw.html.create('table')
-- @return {string}
:addClass('nowraplinks')
--
:addClass(args.bodyclass)
local function docs( args )
local frame = mw.getCurrentFrame()
local title = mw.title.getCurrentTitle()
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]]
tbl
if base ~= 'Navbox' and
:addClass('collapsible')
-- in template ns
:addClass(args.state or 'autocollapse')
ns == 'Template' and
end
-- not a navbox group within a navbox
 
not yesno( args.subgroup ) and
tbl:css('border-spacing', 0)
-- not a collapsible navbox within a navbox
if border == 'subgroup' or border == 'none' then
not yesno( args.collapsible ) and
tbl
-- not if the doc argument is not set to "yes"
:addClass('navbox-subgroup')
yesno( args.doc, false )
:cssText(args.bodystyle)
then
:cssText(args.style)
return frame:expandTemplate{ title = 'Navbox/doc' }
else  -- regular navbox - bodystyle and style will be applied to the wrapper table
else
tbl
return ''
: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
 
--
for k, _ in pairs(args) do
-- @param _args {table}
if type(k) == 'string' then
-- @return {string}
local listnum = k:match('^list(%d+)$')
--
if listnum then table.insert(listnums, tonumber(listnum)) end
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
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 ) then
tbl = header( tbl, args )
end
end


-- render the main body of the navbox
-- insert up to 20 rows
local tbl = renderMainTable()
--
-- 20 is a limit inherited from wikipedia when we copied this over
-- and we've never had a reason to extend it
for i = 1, 20 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()
local gcats = {}
if border == 'none' then
for p = 1, 20 do
local nav = res:tag('div')
local q = tostring ( p )
:attr('role', 'navigation')
:node(tbl)
if args['g' .. j .. 'cat' .. q] then
if args.title then
table.insert(gcats, args['g' .. j .. 'cat' .. q])
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title))
else
else
break
nav:attr('aria-label', 'Navbox')
end
end
end
elseif border == 'subgroup' then
-- We assume that this navbox is being rendered in a list cell of a parent navbox, and is
tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], gcats, args['style' .. j], args.name, args.subgroup )
-- 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
else
nav:attr('aria-label', 'Navbox')
break
end
end
end
end


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


return striped(tostring(res))
tbl = tostring( tbl )
end


function p.navbox(frame)
local cats = ''
if not getArgs then
if not yesno(args.subgroup) and not yesno(args.hidecat) then
getArgs = require('Module:Arguments').getArgs
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 15:08, 14 November 2023

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

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

local p = {}
local tnavbar = require( 'Module:Tnavbar' )
local yesno = require( 'Module:Yesno' )
local onmain = require('Module:Mainonly').on_main()
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 ) and 'navbox-subgroup' or 'navbox' )
		:addClass( 'nowraplinks' )

	if not yesno( args.subgroup ) 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 ) 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( {
			['cellspacing'] = '0',
			['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] = '<span style="display:none;">RS3 </span>'..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 div = insertRow( tbl )
		:tag( 'th' )
			:attr( 'colspan', '2' )
			:addClass( 'navbox-title' )
			:attr( 'id' , 'navbox-title' )
				:tag( 'div' )

	-- @todo move this to site css so we can simplify this (hook off a class)
	-- to something like div:wikitext( args.name and navbar( args ) or args.title )
	-- which can be appended to the above and returned straight away
	if args.name then
		div
			:css( 'padding-right', args.state == 'plain' and '6em' or '0' )
			:wikitext( navbar( args ) )
	else
		div
			:css( 'padding-left', args.state == 'plain' and '0' or '6em' )
			:wikitext( '<span style="display:none;">RS3 </span>'..args.title )
	end

	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 gcats {table}
-- @param style {string}
-- @return {mw.html table}
--
local function row( tbl, gtitle, group, gtype, gcats, 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://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://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://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',
				['border-bottom'] = '0'
			} )
	end

	if style then
		td:cssText( style )
	end
	
	-- add subgroup categories
	if next(gcats) and onmain then
		first_char = page_title:sub(1,1)
		title_pattern = "[" .. first_char:upper() .. first_char:lower() .. "]" .. page_title:sub(2)
		title_pattern = title_pattern :gsub("%(","%%(")
								:gsub("%)","%%)")
								:gsub("_"," ")
								:gsub(" ","[_ ]")
								:gsub("%-","%%-")
		local link_patterns = {
			"%[%["..title_pattern.."%]%]", "%[%["..title_pattern.."%|", "{{[Pp]link%|"..title_pattern.."}}",
			"{{[Pp]link%|"..title_pattern.."%|", "{{[Pp]linkp%|"..title_pattern.."}}",
			"{{[Pp]linkp%|"..title_pattern.."%|", "{{[Ii]linkp%|"..title_pattern.."}}",
			"{{[Ii]linkp%|"..title_pattern.."%|", "{{[Cc]hatl%|"..title_pattern.."}}",
			"{{[Cc]hatl%|"..title_pattern.."%|"
		}
		for _,v in ipairs(link_patterns) do
			if group:match(v) then
				for _,cat in ipairs(gcats) do
					td:wikitext('[[Category:'..cat..']]')
				end
				break
			end
		end
	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:Navigational 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:Navigational 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 ) and
		-- not a collapsible navbox within a navbox
		not yesno( args.collapsible ) 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 ) then
		tbl = header( tbl, args )
	end

	-- insert up to 20 rows
	--
	-- 20 is a limit inherited from wikipedia when we copied this over
	-- and we've never had a reason to extend it
	for i = 1, 20 do
		j = tostring( i )

		if args['group' .. j] then
			local gcats = {}
			for p = 1, 20 do
				local q = tostring ( p )
				
				if args['g' .. j .. 'cat' .. q] then
					table.insert(gcats, args['g' .. j .. 'cat' .. q])
				else
					break
				end
			end
					
			tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], gcats, 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) and not yesno(args.hidecat) 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>