Module:Navbox: Difference between revisions

From PT National Labs
Jump to navigation Jump to search
m 1 revision imported
No edit summary
Tag: Reverted
Line 1: Line 1:
-- <nowiki>
--
--
-- This module implements {{Navbox}}
-- Implements {{Navbox}}
--
--


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


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


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


local function striped(wikitext)
if args.state == 'collapsed' then
-- Return wikitext with markers replaced for odd/even striping.
tbl:addClass( 'mw-collapsed' )
-- Child (subgroup) navboxes are flagged with a category that is removed
elseif args.state == 'uncollapsed' then
-- by parent navboxes. The result is that the category shows all pages
tbl:addClass('navbox-uncollapsed')
-- where a child navbox is not contained in a parent navbox.
local orphanCat = '[[Category:Navbox orphans]]'
if border == 'subgroup' and args.orphan ~= 'yes' then
-- No change; striping occurs in outermost navbox.
return wikitext .. orphanCat
end
local first, second = 'odd', 'even'
if args.evenodd then
if args.evenodd == 'swap' then
first, second = second, first
else
else
first = args.evenodd
tbl:addClass( 'mw-collapsed' )
second = first
tbl:addClass( 'navbox-autocollapse' )
end
end
end
end
local changer
 
if first == second then
if yesno(args.collapsible, false) then
changer = first
tbl:addClass( 'navbox-collapsible' )
else
local index = 0
changer = function (code)
if code == '0' then
-- Current occurrence is for a group before a nested table.
-- Set it to first as a valid although pointless class.
-- The next occurrence will be the first row after a title
-- in a subgroup and will also be first.
index = 0
return first
end
index = index + 1
return index % 2 == 1 and first or second
end
end
end
local regex = orphanCat:gsub('([%[%]])', '%%%1')
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer))  -- () omits gsub count
end


local function processItem(item, nowrapitems)
if args.style then
if item:sub(1, 2) == '{|' then
tbl:cssText( args.style )
-- Applying nowrap to lines in a table does not make sense.
-- Add newlines to compensate for trim of x in |parm=x in a template.
return '\n' .. item ..'\n'
end
if nowrapitems == 'yes' then
local lines = {}
for line in (item .. '\n'):gmatch('([^\n]*)\n') do
local prefix, content = line:match('^([*:;#]+)%s*(.*)')
if prefix and not content:match('^<span class="nowrap">') then
line = prefix .. '<span class="nowrap">' .. content .. '</span>'
end
table.insert(lines, line)
end
item = table.concat(lines, '\n')
end
if item:match('^[*:;#]') then
return '\n' .. item ..'\n'
end
end
return item
end


-- Separate function so that we can evaluate properly whether hlist should
-- used by [[MediaWiki:Gadget-navbox-tracking.js]] for tracking purposes
-- be added by the module
tbl:attr( { ['data-navbox-name'] = args.name } )
local function has_navbar()
return args.navbar ~= 'off' and args.navbar ~= 'plain' and not
(not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox')
end
 
local function renderNavBar(titleCell)
 
if has_navbar() then
titleCell:wikitext(navbar{
args.name,
-- we depend on this being mini = 1 when the navbox module decides
-- to add hlist templatestyles. we also depend on navbar outputting
-- a copy of the hlist templatestyles.
mini = 1,
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;box-shadow:none; padding:0;'
})
end


return tbl
end
end


--
--
--   Title row
-- Wrapper for [[Module:Tnavbar]]
--
-- @param args {table}
-- @return {mw.html table}
--
--
local function renderTitleRow(tbl)
local function navbar( args )
if not args.title then return end


local titleRow = tbl:tag('tr')
local div = mw.html.create( 'div' )
 
:css( {
if args.titlegroup then
float = 'left',
titleRow
['text-align'] = 'left'
:tag('th')
} )
:attr('scope', 'row')
:wikitext( tnavbar._navbar( { [1] = args.name, ['mini'] = true, ['talk'] = 'autoconfirmed' } ) )
:addClass('navbox-group')
return div
:addClass(args.titlegroupclass)
:cssText(args.basestyle)
:cssText(args.groupstyle)
:cssText(args.titlegroupstyle)
:wikitext(args.titlegroup)
end
 
local titleCell = titleRow:tag('th'):attr('scope', 'col')
 
if args.titlegroup then
titleCell
:addClass('navbox-title1')
end
 
local titleColspan = 2
if args.imageleft then titleColspan = titleColspan + 1 end
if args.image then titleColspan = titleColspan + 1 end
if args.titlegroup then titleColspan = titleColspan - 1 end
 
titleCell
:cssText(args.basestyle)
:cssText(args.titlestyle)
:addClass('navbox-title')
:attr('colspan', titleColspan)
 
renderNavBar(titleCell)
 
titleCell
:tag('div')
-- id for aria-labelledby attribute
:attr('id', mw.uri.anchorEncode(args.title))
:addClass(args.titleclass)
:css('font-size', '114%')
:css('margin', '0 4em')
:wikitext(processItem(args.title))
end
end


--
--
--   Above/Below rows
-- Creates the header (what you see when the navbox is collapsed)
--
--
-- @param tbl {mw.html table}
-- @param args {table}
-- @return {mw.html table}
--
local function header( tbl, args )
local div = insertRow( tbl )
:tag( 'th' )
:attr( 'colspan', '2' )
:addClass( 'navbox-title' )
:wikitext( args.name and tostring( navbar( args ) ) )
:tag('div')
:addClass( 'navbox-title-name' )
:wikitext( args.title )


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


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


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


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


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


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


if index == 1 and args.imageleft then
group = '\n' .. table.concat( spl, '\n' )
row
:tag('td')
:addClass('navbox-image')
:addClass(args.imageclass)
:css('width', '1px')              -- Minimize width
:css('padding', '0px 2px 0px 0px')
:cssText(args.imageleftstyle)
:attr('rowspan', #listnums)
:tag('div')
:wikitext(processItem(args.imageleft))
end
end


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


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


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


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


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


if args['group' .. listnum] then
-- group3 = mw.ustring.gsub(group2,w,rep)
listCell
-- end
:addClass('navbox-list1')
--end
else
listCell:attr('colspan', 2)
end


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


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


local listText = args['list' .. listnum]
if style then
local oddEven = ODD_EVEN_MARKER
td:cssText( style )
if listText:sub(1, 12) == '</div><table' then
-- Assume list text is for a subgroup navbox so no automatic striping for this row.
oddEven = listText:find('<th[^>]*"navbox%-title"') and RESTART_MARKER or 'odd'
end
end
listCell
:css('padding', '0px')
:cssText(args.liststyle)
:cssText(rowstyle)
:cssText(args['list' .. listnum .. 'style'])
:addClass('navbox-list')
:addClass('navbox-' .. oddEven)
:addClass(args.listclass)
:addClass(args['list' .. listnum .. 'class'])
:tag('div')
:css('padding', (index == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
:wikitext(processItem(listText, args.nowrapitems))


if index == 1 and args.image then
return td:allDone()
row
:tag('td')
:addClass('navbox-image')
:addClass(args.imageclass)
:css('width', '1px')              -- Minimize width
:css('padding', '0px 0px 0px 2px')
:cssText(args.imagestyle)
:attr('rowspan', #listnums)
:tag('div')
:wikitext(processItem(args.image))
end
end
end


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


local function needsHorizontalLists()
if args.fstyle then
if border == 'subgroup' or args.tracking == 'no' then
th:cssText( args.fstyle )
return false
end
end
local listClasses = {
['plainlist'] = true, ['hlist'] = true, ['hlist hnum'] = true,
['hlist hwrap'] = true, ['hlist vcard'] = true, ['vcard hlist'] = true,
['hlist vevent'] = true,
}
return not (listClasses[args.listclass] or listClasses[args.bodyclass])
end


-- there are a lot of list classes in the wild, so we have a function to find
if mw.ustring.match( args.footer, '^%s*%*' ) then
-- them and add their TemplateStyles
th:newline()
local function addListStyles()
 
local frame = mw.getCurrentFrame()
-- trim whitespace on bullets
-- TODO?: Should maybe take a table of classes for e.g. hnum, hwrap as above
local spl = mw.text.split( args.footer, '\n' )
-- I'm going to do the stupid thing first though
 
-- Also not sure hnum and hwrap are going to live in the same TemplateStyles
for i = 1, #spl do
-- as hlist
spl[i] = mw.text.trim( spl[i] )
local function _addListStyles(htmlclass, templatestyles)
local class_args = { -- rough order of probability of use
'bodyclass', 'listclass', 'aboveclass', 'belowclass', 'titleclass',
'navboxclass', 'groupclass', 'titlegroupclass', 'imageclass'
}
local patterns = {
'^' .. htmlclass .. '$',
'%s' .. htmlclass .. '$',
'^' .. htmlclass .. '%s',
'%s' .. htmlclass .. '%s'
}
local found = false
for _, arg in ipairs(class_args) do
for _, pattern in ipairs(patterns) do
if mw.ustring.find(args[arg] or '', pattern) then
found = true
break
end
end
if found then break end
end
if found then
return frame:extensionTag{
name = 'templatestyles', args = { src = templatestyles }
}
else
return ''
end
end
end
local hlist_styles = ''
-- navbar always has mini = 1, so here (on this wiki) we can assume that
-- we don't need to output hlist styles in navbox again.
if not has_navbar() then
hlist_styles = _addListStyles('hlist', 'Flatlist/styles.css')
end
local plainlist_styles = _addListStyles('plainlist', 'Plainlist/styles.css')
return hlist_styles .. plainlist_styles
end


local function hasBackgroundColors()
args.footer = table.concat( spl, '\n' )
for _, key in ipairs({'titlestyle', 'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
if tostring(args[key]):find('background', 1, true) then
return true
end
end
end


local function hasBorders()
th:addClass( 'navbox-list' )
for _, key in ipairs({'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
if tostring(args[key]):find('border', 1, true) then
return true
end
end
end
end


local function isIllegible()
th:wikitext( args.footer )
-- require('Module:Color contrast') absent on mediawiki.org
return false
end


local function getTrackingCategories()
return th:allDone()
local cats = {}
if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
if hasBackgroundColors() then table.insert(cats, 'Navboxes using background colours') end
if isIllegible() then table.insert(cats, 'Potentially illegible navboxes') end
if hasBorders() then table.insert(cats, 'Navboxes using borders') end
return cats
end
end


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


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


--
--
--   Main navbox tables
-- Adds [[Template:Navbox/doc]] to navbox template pages
--
--
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
not yesno(args.subgroup, false) and
-- not a collapsible navbox within a navbox
not yesno(args.collapsible, false) and
-- not if the doc argument is not set to "yes"
yesno(args.doc, false)
then
return frame:expandTemplate{ title = 'Navbox/doc' }
else
return ''
end
end


tbl:css('border-spacing', 0)
end
if border == 'subgroup' or border == 'none' then
tbl
:addClass('navbox-subgroup')
:cssText(args.bodystyle)
:cssText(args.style)
else  -- regular navbox - 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)
-- Navbox method to allow it to be called by other modules
for i, listnum in ipairs(listnums) do
--
renderListRow(tbl, i, listnum)
-- @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
end
renderBelowRow(tbl)
return tbl
end


function p._navbox(navboxArgs)
local tbl = createTbl( args )
args = navboxArgs
listnums = {}


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


border = mw.text.trim(args.border or args[1] or '')
-- insert up to 25 rows
if border == 'child' then
for i = 1, 25 do
border = 'subgroup'
j = tostring( i )
end


-- render the main body of the navbox
if args['group' .. j] then
local tbl = renderMainTable()
tbl = row( tbl, args['gtitle' .. j], args['group' .. j], args['gtype' .. j], args['style' .. j], args.name, args.subgroup )
-- get templatestyles
local frame = mw.getCurrentFrame()
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Navbox/styles.css' }
}
local templatestyles = ''
if args.templatestyles and args.templatestyles ~= '' then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args.templatestyles }
}
end
local res = mw.html.create()
-- 'navbox-styles' exists for two reasons:
--  1. To wrap the styles to work around phab: T200206 more elegantly. Instead
--   of combinatorial rules, this ends up being linear number of CSS rules.
--  2. To allow MobileFrontend to rip the styles out with 'nomobile' such that
--    they are not dumped into the mobile view.
res:tag('div')
:addClass('navbox-styles')
:addClass('nomobile')
:wikitext(base_templatestyles .. templatestyles)
:done()
-- render the appropriate wrapper around the navbox, depending on the border param
if border == 'none' then
local nav = res:tag('div')
:attr('role', 'navigation')
:wikitext(addListStyles())
:node(tbl)
-- aria-labelledby title, otherwise above, otherwise lone group
if args.title or args.above or (args.group1 and not args.group2) then
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title or args.above or args.group1))
else
else
nav:attr('aria-label', 'Navbox')
break
end
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>')
:wikitext(addListStyles())
: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')
:wikitext(addListStyles())
: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))
tbl = tostring( tbl )
end


function p.navbox(frame)
local cats = ''
if not getArgs then
if not yesno(args.subgroup, false) and not yesno(args.hidecat, false) then
getArgs = require('Module:Arguments').getArgs
cats = categories()
end
args = getArgs(frame, {wrappers = {'Template:Navbox', 'Template:Navbox subgroup'}})
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>

Revision as of 10:22, 26 June 2025

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 page_title = mw.title.getCurrentTitle().fullText
--
-- Helper for inserting a new row into the navbox
--
-- @param tbl {mw.html table}
-- @return tbl {mw.html table}
--
local function insertRow( tbl )
	return tbl:tag( 'tr' )
end

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

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

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

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

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

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

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

	-- used by [[MediaWiki:Gadget-navbox-tracking.js]] for tracking purposes
	tbl:attr( { ['data-navbox-name'] = args.name } )

	return tbl
end

--
-- Wrapper for [[Module:Tnavbar]]
--
-- @param args {table}
-- @return {mw.html table}
--
local function navbar( args )

	local div = mw.html.create( 'div' )
		:css( {
			float = 'left',
			['text-align'] = 'left'
		} )
		:wikitext( tnavbar._navbar( { [1] = args.name, ['mini'] = true, ['talk'] = 'autoconfirmed' } ) )
	return div
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' )
				:wikitext( args.name and tostring( navbar( args ) ) )
				:tag('div')
					:addClass( 'navbox-title-name' )
					:wikitext( args.title )

	return div:allDone()
end

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

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

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

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

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

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

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

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

	--			local newlink = ''

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

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

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

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

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

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

	if style then
		td:cssText( style )
	end

	return td:allDone()
end

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

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

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

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

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

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

		th:addClass( 'navbox-list' )
	end

	th:wikitext( args.footer )

	return th:allDone()
end

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

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

end

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

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

end

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

	local tbl = createTbl( args )

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

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

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

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

	tbl = tostring( tbl )

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

	return tbl .. cats .. docs
end

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

return p
-- </nowiki>