Difference between revisions of "Module:Navbox"

From The Kingkiller Chronicle Wiki
Jump to navigation Jump to search
(Import from Wikipedia)
 
(Wikipedia's navbox isn't working. Probably some weird dependecy -_-)
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
  
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 tableRowAdded = false
 
local border
 
local listnums = {}
 
  
local function trim(s)
+
tbl
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
+
:addClass( yesno( args.subgroup ) and 'navbox-subgroup' or 'navbox' )
end
+
: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
  
local function addNewline(s)
+
if yesno( args.collapsible ) then
    if s:match('^[*:;#]') or s:match('^{|') then
+
tbl:addClass( 'navbox-collapsible' )
        return '\n' .. s ..'\n'
+
end
    else
 
        return s
 
    end
 
end
 
  
local function addTableRow(tbl)
+
if args.style then
    -- If any other rows have already been added, then we add a 2px gutter row.
+
tbl:cssText( args.style )
    if tableRowAdded then
+
end
        tbl
 
            :tag('tr')
 
                :css('height', '2px')
 
                :tag('td')
 
                    :attr('colspan',2)
 
    end
 
  
    tableRowAdded = true
+
-- manually set collapse/expand messages
 +
-- bug causing the default database messages to be used
 +
tbl
 +
:attr( {
 +
['data-expandtext'] = 'show',
 +
['data-collapsetext'] = 'hide'
 +
} )
  
    return tbl:tag('tr')
+
return tbl
 
end
 
end
  
local function renderNavBar(titleCell)
+
--
    -- Depending on the presence of the navbar and/or show/hide link, we may need to add a spacer div on the left
+
-- Wrapper for [[Module:Tnavbar]]
    -- or right to keep the title centered.
+
--
    local spacerSide = nil
+
-- @param args {table}
 +
-- @return {string}
 +
--
 +
local function navbar( args )
 +
return tnavbar._collapsible( { [1] = args.title, [2] = args.name } )
 +
end
  
    if args.navbar == 'off' then
+
--
        -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's
+
-- Creates the header (what you see when the navbox is collapsed)
        -- also no show/hide link, then we need a spacer on the right to achieve the left shift.
+
--
        if args.state == 'plain' then spacerSide = 'right' end
+
-- @param tbl {mw.html table}
    elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle() == 'Template:Navbox' and (border == 'subgroup' or border == 'child' or border == 'none')) then
+
-- @param args {table}
        -- No navbar. Need a spacer on the left to balance out the width of the show/hide link.
+
-- @return {mw.html table}
        if args.state ~= 'plain' then spacerSide = 'left' end
+
--
    else
+
local function header( tbl, args )
        -- Will render navbar (or error message). If there's no show/hide link, need a spacer on the right
+
local div = insertRow( tbl )
        -- to balance out the width of the navbar.
+
:tag( 'th' )
        if args.state == 'plain' then spacerSide = 'right' end
+
:attr( 'colspan', '2' )
 +
:addClass( 'navbox-title' )
 +
:attr( 'id' , 'navbox-title' )
 +
:tag( 'div' )
  
        titleCell:wikitext(navbar{
+
-- @todo move this to site css so we can simplify this (hook off a class)
            args.name,
+
-- to something like div:wikitext( args.name and navbar( args ) or args.title )
            mini = 1,
+
-- which can be appended to the above and returned straight away
            fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') ..  ';background:none transparent;border:none;'
+
if args.name then
        })
+
div
    end
+
: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( args.title )
 +
end
  
    -- Render the spacer div.
+
return div:allDone()
    if spacerSide then
 
        titleCell
 
            :tag('span')
 
                :css('float', spacerSide)
 
                :css('width', '6em')
 
                :wikitext('&nbsp;')
 
    end
 
 
end
 
end
  
 
--
 
--
--   Title row
+
-- 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 renderTitleRow(tbl)
+
local function row( tbl, gtitle, group, gtype, style, _name )
    if not args.title then return end
+
local tr = insertRow( tbl )
 +
local td
 +
 
 +
if gtitle then
 +
td = tr
 +
:tag( 'td' )
 +
:addClass( 'navbox-group' )
 +
:wikitext( gtitle )
 +
:done()
 +
:tag( 'td' )
 +
else
 +
td = tr
 +
:tag( 'td' )
 +
:attr( 'colspan', '2' )
 +
end
  
    local titleRow = addTableRow(tbl)
+
--[[
 +
  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()
  
    if args.titlegroup then
+
-- trim whitespace on bullets
        titleRow
+
local spl = mw.text.split( group, '\n' )
            :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')
+
for i = 1, #spl do
 +
spl[i] = mw.text.trim( spl[i] )
 +
end
  
    if args.titlegroup then
+
group = table.concat( spl, '\n' )
        titleCell
+
end
            :css('border-left', '2px solid #fdfdfd')
 
            :css('width', '100%')
 
    end
 
  
    local titleColspan = 2
+
--local group2 = group
    if args.imageleft then titleColspan = titleColspan + 1 end
+
--local group3 = group2
    if args.image then titleColspan = titleColspan + 1 end
+
-- analytics
    if args.titlegroup then titleColspan = titleColspan - 1 end
 
  
    titleCell
+
--if _name then
        :cssText(args.basestyle)
+
-- local name = mw.ustring.gsub(_name,' ','_')
        :cssText(args.titlestyle)
+
-- for v in mw.ustring.gmatch(group,'%[%[[^%]]+%]%]') do
        :addClass('navbox-title')
+
-- if mw.ustring.match(v,'%[%[File:.+|link=') then
        :attr('colspan', titleColspan)
+
-- 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
  
    renderNavBar(titleCell)
+
-- local newlink = ''
  
    titleCell
+
-- black links if current page
        :tag('div')
+
-- if link == page_title then
            :addClass(args.titleclass)
+
-- newlink = string.format('<b>%s</b>',txt)
            :css('font-size', '110%')
+
-- else
            :wikitext(addNewline(args.title))
+
-- local _link = mw.ustring.gsub(link or '',' ','_')
end
+
-- 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
--   Above/Below rows
+
-- local rep = mw.ustring.gsub(v,'%]','')
--
+
-- rep = rep..']'
 +
-- local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
  
local function getAboveBelowColspan()
+
-- group3 = mw.ustring.gsub(group2,w,rep)
    local ret = 2
+
-- end
    if args.imageleft then ret = ret + 1 end
+
--end
    if args.image then ret = ret + 1 end
 
    return ret
 
end
 
  
local function renderAboveRow(tbl)
+
td
    if not args.above then return end
+
:addClass( 'navbox-list' )
 +
:wikitext( group ) --group3
  
    addTableRow(tbl)
+
if gtype and mw.ustring.lower( gtype ) == 'subgroup' then
        :tag('td')
+
td
            :addClass('navbox-abovebelow')
+
:css( {
            :addClass(args.aboveclass)
+
padding = '0',
            :cssText(args.basestyle)
+
['border-bottom'] = '0'
            :cssText(args.abovestyle)
+
} )
            :attr('colspan', getAboveBelowColspan())
+
end
            :tag('div')
 
                :wikitext(addNewline(args.above))
 
end
 
  
local function renderBelowRow(tbl)
+
if style then
    if not args.below then return end
+
td:cssText( style )
 +
end
  
    addTableRow(tbl)
+
return td:allDone()
        :tag('td')
 
            :addClass('navbox-abovebelow')
 
            :addClass(args.belowclass)
 
            :cssText(args.basestyle)
 
            :cssText(args.belowstyle)
 
            :attr('colspan', getAboveBelowColspan())
 
            :tag('div')
 
                :wikitext(addNewline(args.below))
 
 
end
 
end
  
 
--
 
--
--   List rows
+
-- Inserts a footer into the navbox
 +
--
 +
-- @param tbl {mw.html table}
 +
-- @param args {table}
 +
-- @return {mw.html table}
 
--
 
--
local function renderListRow(tbl, listnum)
+
local function footer( tbl, args )
    local row = addTableRow(tbl)
+
local th = insertRow( tbl )
 
+
:tag( 'th' )
    if listnum == 1 and args.imageleft then
+
:attr( 'colspan', '2' )
        row
+
:addClass( 'navbox-footer' )
            :tag('td')
 
                :addClass('navbox-image')
 
                :addClass(args.imageclass)
 
                :css('width', '0%')
 
                :css('padding', '0px 2px 0px 0px')
 
                :cssText(args.imageleftstyle)
 
                :attr('rowspan', 2 * #listnums - 1)
 
                :tag('div')
 
                    :wikitext(addNewline(args.imageleft))
 
    end
 
  
    if args['group' .. listnum] then
+
if args.fstyle then
        local groupCell = row:tag('th')
+
th:cssText( args.fstyle )
 +
end
  
        groupCell
+
if mw.ustring.match( args.footer, '^%s*%*' ) then
            :attr('scope', 'row')
+
th:newline()
            :addClass('navbox-group')
 
            :addClass(args.groupclass)
 
            :cssText(args.basestyle)
 
  
        if args.groupwidth then
+
-- trim whitespace on bullets
            groupCell:css('width', args.groupwidth)
+
local spl = mw.text.split( args.footer, '\n' )
        end
 
  
        groupCell
+
for i = 1, #spl do
            :cssText(args.groupstyle)
+
spl[i] = mw.text.trim( spl[i] )
            :cssText(args['group' .. listnum .. 'style'])
+
end
            :wikitext(args['group' .. listnum])
 
    end
 
  
    local listCell = row:tag('td')
+
args.footer = table.concat( spl, '\n' )
  
    if args['group' .. listnum] then
+
th:addClass( 'navbox-list' )
        listCell
+
end
            :css('text-align', 'left')
 
            :css('border-left-width', '2px')
 
            :css('border-left-style', 'solid')
 
    else
 
        listCell:attr('colspan', 2)
 
    end
 
  
    if not args.groupwidth then
+
th:wikitext( args.footer )
        listCell:css('width', '100%')
 
    end
 
  
    local isOdd = (listnum % 2) == 1
+
return th:allDone()
    local rowstyle = args.evenstyle
 
    if isOdd then rowstyle = args.oddstyle end
 
 
 
    local evenOdd
 
    if args.evenodd == 'swap' then
 
        if isOdd then evenOdd = 'even' else evenOdd = 'odd' end
 
    else
 
        if isOdd then evenOdd = args.evenodd or 'odd' else evenOdd = args.evenodd or 'even' end
 
    end
 
 
 
    listCell
 
        :css('padding', '0px')
 
        :cssText(args.liststyle)
 
        :cssText(rowstyle)
 
        :cssText(args['list' .. listnum .. 'style'])
 
        :addClass('navbox-list')
 
        :addClass('navbox-' .. evenOdd)
 
        :addClass(args.listclass)
 
        :tag('div')
 
            :css('padding', (listnum == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
 
            :wikitext(addNewline(args['list' .. listnum]))
 
 
 
    if listnum == 1 and args.image then
 
        row
 
            :tag('td')
 
                :addClass('navbox-image')
 
                :addClass(args.imageclass)
 
                :css('width', '0%')
 
                :css('padding', '0px 0px 0px 2px')
 
                :cssText(args.imagestyle)
 
                :attr('rowspan', 2 * #listnums - 1)
 
                :tag('div')
 
                    :wikitext(addNewline(args.image))
 
    end
 
 
end
 
end
  
 
 
--
 
--
--   Tracking categories
+
-- 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
  
local function needsHorizontalLists()
+
if ns == 'Template' then
    if border == 'child' or border == 'subgroup' or args.tracking == 'no' then return false end
+
-- sort in category by pagename
 +
return '[[Category:Navigational templates|' .. page .. ']]'
 +
else
 +
return ''
 +
end
  
    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent'}
 
    for i, cls in ipairs(listClasses) do
 
        if args.listclass == cls or args.bodyclass == cls then
 
            return false
 
        end
 
    end
 
 
    return true
 
 
end
 
end
  
local function hasBackgroundColors()
+
--
    return mw.ustring.match(args.titlestyle or '','background') or mw.ustring.match(args.groupstyle or '','background') or mw.ustring.match(args.basestyle or '','background')
+
-- Adds [[Template:Navbox/doc]] to navbox template pages
end
+
--
 +
-- @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
  
local function getTrackingCategories()
+
-- not if a subpage of [[Template:Navbox]]
    local cats = {}
+
if base ~= 'Navbox' and
    if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
+
-- in template ns
    if hasBackgroundColors() then table.insert(cats, 'Navboxes using background colors') end
+
ns == 'Template' and
    return cats
+
-- not a navbox group within a navbox
end
+
not yesno( args.subgroup ) and
 +
-- not a collapsible navbox within a navbox
 +
not yesno( args.collapsible ) and
 +
-- not if the doc argument is set to "no"
 +
( args.doc == nil or yesno( args.doc ) )
 +
then
 +
return frame:expandTemplate{ title = 'Navbox/doc' }
 +
else
 +
return ''
 +
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 i, cat in ipairs(getTrackingCategories()) do
 
        builder:wikitext('[[Category:' .. cat .. ']]')
 
    end
 
 
end
 
end
  
 
--
 
--
--   Main navbox tables
+
-- Navbox method to allow it to be called by other modules
 +
--
 +
-- @param _args {table}
 +
-- @return {string}
 
--
 
--
local function renderMainTable()
+
function p._navbox( _args )
    local tbl = mw.html.create('table')
+
local args = {}
        :addClass('nowraplinks')
+
local wkCss = ''
        :addClass(args.bodyclass)
+
local wkDiv = ''
 
+
local j
    if args.title and args.state and (args.state ~= 'plain' and args.state ~= 'off') then
+
        tbl
+
-- preserves parser function behaviour where an empty string is considered undefined
            :addClass('mw-collapsible')
+
-- or nil in lua's case
            :addClass('mw-collapsed')
+
for k, v in pairs( _args ) do
    end
+
if v ~= '' then
 
+
args[k] = v
    tbl:css('border-spacing', 0)
+
end
    if border == 'subgroup' or border == 'child' or border == 'none' then
+
end
        tbl
 
            :addClass('navbox-subgroup')
 
            :cssText(args.bodystyle)
 
            :cssText(args.style)
 
    else -- regular navobx - bodystyle and style will be applied to the wrapper table
 
        tbl
 
            :addClass('navbox-inner')
 
            :css('background', 'transparent')
 
            :css('color', 'inherit')
 
    end
 
    tbl:cssText(args.innerstyle)
 
 
 
    renderTitleRow(tbl)
 
    renderAboveRow(tbl)
 
    for i, listnum in ipairs(listnums) do
 
        renderListRow(tbl, listnum)
 
    end
 
    renderBelowRow(tbl)
 
  
    return tbl
+
local tbl = createTbl( args )
end
 
  
function p._navbox(navboxArgs)
+
if not yesno( args.subgroup ) then
    args = navboxArgs
+
tbl = header( tbl, args )
 +
end
  
    for k, v in pairs(args) do
+
-- insert up to 20 rows
        local listnum = ('' .. k):match('^list(%d+)$')
+
--
        if listnum then table.insert(listnums, tonumber(listnum)) end
+
-- 20 is a limit inherited from wikipedia when we copied this over
    end
+
-- and we've never had a reason to extend it
    table.sort(listnums)
+
for i = 1, 20 do
 +
j = tostring( i )
  
    border = trim(args.border or args[1] or '')
+
if args['group' .. j] then
 +
tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], args['style' .. j], args.name )
 +
else
 +
break
 +
end
 +
end
  
    -- render the main body of the navbox
+
if args.footer then
    local tbl = renderMainTable()
+
tbl = footer( tbl, args )
 +
end
  
    -- render the appropriate wrapper around the navbox, depending on the border param
+
tbl = tostring( tbl )
    local res = mw.html.create()
 
    if border == 'none' then
 
        res:node(tbl)
 
    elseif border == 'subgroup' or border == 'child' 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>') -- XXX: hack due to lack of unclosed support in mw.html.
 
            :node(tbl)
 
            :wikitext('<div>') -- XXX: hack due to lack of unclosed support in mw.html.
 
    else
 
        res
 
            :tag('table')
 
                :addClass('navbox')
 
                :css('border-spacing', 0)
 
                :cssText(args.bodystyle)
 
                :cssText(args.style)
 
                :tag('tr')
 
                    :tag('td')
 
                        :css('padding', '2px')
 
                        :node(tbl)
 
    end
 
  
    renderTrackingCategories(res)
+
local cats = ''
 +
if not yesno(args.subgroup) and not yesno(args.hidecat) then
 +
cats = categories()
 +
end
 +
local docs = docs( args )
  
    return tostring(res)
+
return tbl .. cats .. docs
 
end
 
end
  
function p.navbox(frame)
+
--
    if not getArgs then
+
-- Main navbox method accessed through #invoke
        getArgs = require('Module:Arguments').getArgs
+
--
    end
+
-- @param frame {table}
    args = getArgs(frame, {wrappers = 'Template:Navbox'})
+
-- @return {string}
 
+
--
    -- Read the arguments in the order they'll be output in, to make references number in the right order.
+
function p.navbox( frame )
    local _
+
local args = frame:getParent().args
    _ = args.title
+
return p._navbox( args )
    _ = args.above
 
    for i = 1, 20 do
 
        _ = args["group" .. tostring(i)]
 
        _ = args["list" .. tostring(i)]
 
    end
 
    _ = args.below
 
 
 
    return p._navbox(args)
 
 
end
 
end
  
 
return p
 
return p
 +
-- </nowiki>

Revision as of 03:49, 31 December 2018

Example navbox:

{{Navbox
|name = adsf
|title = adsf
|gtitle1 = asdf
|group1 =adsf
|gtitle2 = adsf
|group2 = * adsf
|gtitle3 = adsf
|group3 = * adsf
|gtitle4 = adsf
|group4 = * adsf
|gtitle5 = adsf
|group5 = * adsf
|gtitle6 = adsf
|group6 = * adsf
|gtitle7 = adsf
|group7 = * adsf
|gtitle8 = adsf
|group8 = * adsf
|gtitle9 = adsf
|group9 = * adsf
* asdf
}}

Produces:

Note: to use a navbox on multiple pages, make a template (like Template:UnivNav) and use it like this: {{UnivNav}}.


-- <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 ) 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( {
			['data-expandtext'] = 'show',
			['data-collapsetext'] = 'hide'
		} )

	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 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( 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 style {string}
-- @return {mw.html table}
--
local function row( tbl, gtitle, group, gtype, style, _name )
	local tr = insertRow( tbl )
	local td

	if gtitle then
		td = tr
			:tag( 'td' )
				:addClass( 'navbox-group' )
				:wikitext( gtitle )
				:done()
			:tag( 'td' )
	else
		td = tr
			: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 = 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
			:css( {
				padding = '0',
				['border-bottom'] = '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: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 set to "no"
		( args.doc == nil or yesno( args.doc ) )
	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
			tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], args['style' .. j], args.name )
		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>