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
 
(14 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
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
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; padding:0;'
})
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
end


local titleCell = titleRow:tag('th'):attr('scope', 'col')
return div:allDone()
 
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
-- 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')
-- id for aria-labelledby attribute, if no title
:attr('id', args.title and nil or mw.uri.anchorEncode(args.above))
:wikitext(processItem(args.above, args.nowrapitems))
end


local function renderBelowRow(tbl)
group = '\n' .. table.concat( spl, '\n' )
if not args.below then return end
end


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


--
--if _name then
--   List rows
-- local name = mw.ustring.gsub(_name,' ','_')
--
-- for v in mw.ustring.gmatch(group,'%[%[[^%]]+%]%]') do
local function renderListRow(tbl, index, listnum)
-- if mw.ustring.match(v,'%[%[File:.+|link=') then
local row = tbl:tag('tr')
-- 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


if index == 1 and args.imageleft then
-- local newlink = ''
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


if args['group' .. listnum] then
-- black links if current page
local groupCell = row:tag('th')
-- 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


-- id for aria-labelledby attribute, if lone group with no title or above
--[==[
if listnum == 1 and not (args.title or args.above or args.group2) then
fix [[these kind]]s of [[link]]s post analytics parse
groupCell
]==]
:attr('id', mw.uri.anchorEncode(args.group1))
-- group3 = group2
end


groupCell
-- for v in mw.ustring.gmatch(group2,'%[https://runescape.wiki.com/w[^%]]-%]%a') do
:attr('scope', 'row')
-- local rep = mw.ustring.gsub(v,'%]','')
:addClass('navbox-group')
-- rep = rep..']'
:addClass(args.groupclass)
-- local w = mw.ustring.gsub(v,'([%%%]%[%-^$*()+?])','%%%1')
:cssText(args.basestyle)
:css('width', args.groupwidth or '1%') -- If groupwidth not specified, minimize width


groupCell
-- group3 = mw.ustring.gsub(group2,w,rep)
:cssText(args.groupstyle)
-- end
:cssText(args['group' .. listnum .. 'style'])
--end
:wikitext(args['group' .. listnum])
end


local listCell = row:tag('td')
td
:addClass( 'navbox-list' )
:wikitext( group ) --group3


if args['group' .. listnum] then
if gtype and mw.ustring.lower( gtype ) == 'subgroup' then
listCell
td
:css('text-align', 'left')
:addClass( 'navbox-parent' )
:css('border-left-width', '2px')
:css( {
:css('border-left-style', 'solid')
padding = '0',
else
['border-bottom'] = '0'
listCell:attr('colspan', 2)
} )
end
end


if not args.groupwidth then
if style then
listCell:css('width', '100%')
td:cssText( style )
end
end
 
local rowstyle  -- usually nil so cssText(rowstyle) usually adds nothing
-- add subgroup categories
if index % 2 == 1 then
if next(gcats) and onmain then
rowstyle = args.oddstyle
first_char = page_title:sub(1,1)
else
title_pattern = "[" .. first_char:upper() .. first_char:lower() .. "]" .. page_title:sub(2)
rowstyle = args.evenstyle
title_pattern = title_pattern :gsub("%(","%%(")
end
:gsub("%)","%%)")
 
:gsub("_"," ")
local listText = args['list' .. listnum]
:gsub(" ","[_ ]")
local oddEven = ODD_EVEN_MARKER
:gsub("%-","%%-")
if listText:sub(1, 12) == '</div><table' then
local link_patterns = {
-- Assume list text is for a subgroup navbox so no automatic striping for this row.
"%[%["..title_pattern.."%]%]", "%[%["..title_pattern.."%|", "{{[Pp]link%|"..title_pattern.."}}",
oddEven = listText:find('<th[^>]*"navbox%-title"') and RESTART_MARKER or 'odd'
"{{[Pp]link%|"..title_pattern.."%|", "{{[Pp]linkp%|"..title_pattern.."}}",
end
"{{[Pp]linkp%|"..title_pattern.."%|", "{{[Ii]linkp%|"..title_pattern.."}}",
listCell
"{{[Ii]linkp%|"..title_pattern.."%|", "{{[Cc]hatl%|"..title_pattern.."}}",
:css('padding', '0px')
"{{[Cc]hatl%|"..title_pattern.."%|"
:cssText(args.liststyle)
}
:cssText(rowstyle)
for _,v in ipairs(link_patterns) do
:cssText(args['list' .. listnum .. 'style'])
if group:match(v) then
:addClass('navbox-list')
for _,cat in ipairs(gcats) do
:addClass('navbox-' .. oddEven)
td:wikitext('[[Category:'..cat..']]')
:addClass(args.listclass)
end
:addClass(args['list' .. listnum .. 'class'])
break
:tag('div')
end
:css('padding', (index == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
end
: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
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', '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: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]]
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 ) and
 
-- not a collapsible navbox within a navbox
tbl:css('border-spacing', 0)
not yesno( args.collapsible ) 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
end
tbl:cssText(args.innerstyle)


renderTitleRow(tbl)
renderAboveRow(tbl)
for i, listnum in ipairs(listnums) do
renderListRow(tbl, i, listnum)
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 ) 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
-- aria-labelledby title, otherwise above, otherwise lone group
table.insert(gcats, args['g' .. j .. 'cat' .. q])
if args.title or args.above or (args.group1 and not args.group2) then
else
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title or args.above or args.group1))
break
end
end
tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], gcats, args['style' .. j], args.name, args.subgroup )
else
else
nav:attr('aria-label', 'Navbox')
break
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
nav:attr('aria-label', 'Navbox')
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 = ''
end
if not yesno(args.subgroup) and not yesno(args.hidecat) then
args = getArgs(frame, {wrappers = {'Template:Navbox', 'Template:Navbox subgroup'}})
cats = categories()
if frame.args.border then
-- This allows Template:Navbox_subgroup to use {{#invoke:Navbox|navbox|border=...}}.
args.border = frame.args.border
end
end
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>