Модуль:Navbox
Перейти к навигации
Перейти к поиску
Для документации этого модуля может быть создана страница Модуль:Navbox/doc
local p = {}
local getArgs -- lazily initialized
local yesno -- lazily initialized
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
local RESTART_MARKER = '\127_ODDEVEN0_\127'
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'
-- общие параметры
local commonAliases = {
name = {'name', 'имя'},
navigation = {'navigation', 'навигация'},
navbar = {'navbar', 'gear', 'ссылка_на_просмотр'},
state = {'state', 'type'},
orphan = {'orphan'},
tracking = {'tracking'},
border = {'border', 1},
title = {'title', 'заголовок', 'header'},
titlegroup = {'titlegroup'},
above = {'above', 'вверху'},
image = {'image', 'изображение'},
imageleft = {'imageleft', 'изображение2', 'изображение_слева'},
below = {'below', 'внизу', 'footer'},
evenodd = {'evenodd', 'чётные_нечётные', 'четные_нечетные'},
groupwidth = {'groupwidth', 'ширина_групп'},
listpadding = {'listpadding', 'отступ_списков'},
bodyclass = {'class', 'bodyclass', 'класс_тела'},
titleclass = {'titleclass', 'класс_заголовка'},
titlegroupclass = {'titlegroupclass'},
gearclass = {'gearclass', 'класс_шестеренки', 'класс_шестерёнки'},
aboveclass = {'aboveclass', 'класс_вверху'},
belowclass = {'belowclass', 'класс_внизу'},
groupclass = {'groupclass', 'класс_групп'},
listclass = {'listclass', 'класс_списков'},
imageclass = {'imageclass', 'класс_изображения'},
bodystyle = {'style', 'bodystyle', 'стиль_тела'},
basestyle = {'basestyle', 'стиль', 'стиль_базовый'},
titlestyle = {'titlestyle', 'стиль_основного_заголовка', 'стиль_заголовка'},
titlegroupstyle = {'titlegroupstyle'},
gearstyle = {'gearstyle', 'стиль_шестеренки', 'стиль_шестерёнки'},
innerstyle = {'innerstyle'},
abovestyle = {'abovestyle', 'стиль_вверху'},
belowstyle = {'belowstyle', 'стиль_внизу'},
groupstyle = {'groupstyle', 'стиль_заголовков', 'стиль_групп'},
liststyle = {'liststyle', 'стиль_списков'},
imagestyle = {'imagestyle', 'стиль_изображения'},
imageleftstyle = {'imageleftstyle', 'imagestyle2', 'стиль_изображения_слева'},
}
-- параметры для элементов
local elementAliases = {
group = {'group%s', 'заголовок%s', 'группа%s'},
list = {'list%s', 'список%s', 'value%s'},
groupstyle = {'group%sstyle', 'стиль_заголовка%s', 'стиль_группы%s'},
listclass = {'list%sclass', 'класс%sсписка', 'класс_списка%s'},
liststyle = {'list%sstyle', 'стиль_списка%s'},
listpadding = {'list%spadding'}
}
local function checkAliases(args, aliases, index)
for _, alias in ipairs(aliases) do
local arg
if index then
arg = args[string.format(alias, index)]
else
arg = args[alias]
end
if arg ~= nil then
return arg
end
end
return
end
local function checkElAliases(args, name, index)
return checkAliases(args, elementAliases[name], index)
end
local function striped(wikitext, args)
-- Return wikitext with markers replaced for odd/even striping.
-- Child (subgroup) navboxes are flagged with a category that is removed
-- by parent navboxes. The result is that the category shows all pages
-- where a child navbox is not contained in a parent navbox.
local orphanCat = '[[Категория:Шаблоны:Навигационные шаблоны без родителя]]'
if args.border == 'subgroup' and not args.orphan then
-- No change; striping occurs in outermost navbox.
return wikitext .. orphanCat
end
local first, second = 'odd', 'even'
if args.evenodd then
if args.evenodd == 'swap' then
first, second = second, first
else
first = args.evenodd
second = first
end
end
local changer
if first == second then
changer = first
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
local regex = orphanCat:gsub('([%[%]])', '%%%1')
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer)) -- () omits gsub count
end
local function addNewline(s)
if s:match('^[*:;#]') or s:match('^{|') then
return '\n' .. s ..'\n'
else
return s
end
end
local function renderNavBar(titleCell, args)
if args.navbar == false
or not args.name
or not mw.title.new(args.name)
then
return
end
local gearclass = args.gearclass or 'adapt-to-light-theme'
local gearstyle = args.gearstyle or '--adapt-to-light-theme--invert: 75%'
local gear = mw.ustring.format(
--'[[File:Gear_icon.svg|16px|link=%s|class=%s|Просмотр этого шаблона]]',
'[[File:Gear_icon.png|16px|link=%s|class=%s|Просмотр этого шаблона]]',
mw.title.new(args.name, 10).fullText,
gearclass
)
--- Gear creation
titleCell
:tag('span')
:addClass('navbox-gear')
:css('float', 'left')
:css('text-align', 'left')
:css('width', '5em')
:css('margin-right', '0.5em')
:cssText(gearstyle)
:wikitext(gear)
end
--
-- Title row
--
local function renderTitleRow(tbl, args)
if not args.title then return end
local titleRow = tbl:tag('tr')
if args.titlegroup then
titleRow
:tag('th')
:attr('scope', 'row')
:addClass('navbox-group')
:addClass(args.titlegroupclass)
:cssText(args.basestyle)
:cssText(args.groupstyle)
:cssText(args.titlegroupstyle)
:wikitext(args.titlegroup)
end
local titleCell = titleRow:tag('th'):attr('scope', 'colgroup')
if args.titlegroup then
titleCell
:css('border-left-width', '2px')
:css('border-left-style', 'solid')
: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
local titleMargin = '0 5.5em'
if args.state == 'off'
or (args.state == 'plain' and args.navbar == false)
then
titleMargin = nil
end
titleCell
:cssText(args.basestyle)
:cssText(args.titlestyle)
:addClass('navbox-title')
:attr('colspan', titleColspan)
renderNavBar(titleCell, args)
titleCell
:tag('div')
:attr('id', mw.uri.anchorEncode(args.title))
:addClass(args.titleclass)
:css('font-size', '114%')
:css('margin', titleMargin)
:wikitext(addNewline(args.title))
end
--
-- Above/Below rows
--
local function getAboveBelowColspan(args)
local ret = 2
if args.imageleft then ret = ret + 1 end
if args.image then ret = ret + 1 end
return ret
end
local function renderAboveRow(tbl, args)
if not args.above then return end
tbl:tag('tr')
:tag('td')
:addClass('navbox-abovebelow')
:addClass(args.aboveclass)
:cssText(args.basestyle)
:cssText(args.abovestyle)
:attr('colspan', getAboveBelowColspan(args))
:tag('div')
:wikitext(addNewline(args.above))
end
local function renderBelowRow(tbl, args)
if not args.below then return end
tbl:tag('tr')
:tag('td')
:addClass('navbox-abovebelow')
:addClass('hlist')
:addClass(args.belowclass)
:cssText(args.basestyle)
:cssText(args.belowstyle)
:attr('colspan', getAboveBelowColspan(args))
:tag('div')
:wikitext(addNewline(args.below))
end
--
-- List rows
--
local function renderListRow(tbl, args, index, rowspan, rowArgs)
local row = tbl:tag('tr')
if index == 1 and args.imageleft then
row
:tag('td')
:addClass('navbox-image')
:addClass(args.imageclass)
:css('width', '1px')
:css('padding', '0px 7px 0px 0px')
:cssText(args.imageleftstyle)
:attr('rowspan', rowspan)
:tag('div')
:wikitext(addNewline(args.imageleft))
end
if rowArgs.group then
local groupCell = row:tag('th')
groupCell
:attr('scope', 'row')
:addClass('navbox-group')
:addClass(args.groupclass)
:cssText(args.basestyle)
:css('width', args.groupwidth or '1px') -- If groupwidth not specified, minimize width
:css('white-space', args.groupwidth and 'normal' or nil)
groupCell
:cssText(args.groupstyle)
:cssText(rowArgs.groupstyle)
:wikitext(rowArgs.group)
end
local listCell = row:tag('td')
if rowArgs.group then
listCell
: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
listCell:css('width', '100%')
end
local listText = rowArgs.list
local oddEven = ODD_EVEN_MARKER
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
listCell
:css('padding', '0px')
:cssText(args.liststyle)
:cssText(rowArgs.liststyle)
:addClass('navbox-list')
:addClass('navbox-' .. oddEven)
:addClass(args.listclass)
:addClass(rowArgs.listclass)
:tag('div')
:css('padding', rowArgs.listpadding or args.listpadding or '0em 0.25em')
:wikitext(addNewline(listText))
if index == 1 and args.image then
row
:tag('td')
:addClass('navbox-image')
:addClass(args.imageclass)
:css('width', '1px')
:css('padding', '0px 0px 0px 7px')
:cssText(args.imagestyle)
:attr('rowspan', rowspan)
:tag('div')
:wikitext(addNewline(args.image))
end
end
--
-- Tracking categories
--
local function needsChangetoSubgroups(args)
for i = 1, 23 do
if (checkElAliases(args, 'group', i)) and not (checkElAliases(args, 'list', i)) then
return true
end
end
return false
end
local function needsChangetoSubtitles(args)
for i = 1, 23 do
if args['subtitle' .. i] then
return true
end
end
return false
end
local function needsHorizontalLists(args)
if args.border == 'subgroup' or args.tracking == false then
return false
end
local lc, bc = args.listclass, args.bodyclass
if lc and (' ' .. lc .. ' '):match(' hlist ')
or lc and (' ' .. lc .. ' '):match(' plainlist ')
or bc and (' ' .. bc .. ' '):match(' hlist ')
or bc and (' ' .. bc .. ' '):match(' plainlist ')
then
return false
end
return true
end
local function getTrackingCategories(args)
local cats = {}
if needsChangetoSubgroups(args) then table.insert(cats, 'Навигационные шаблоны с ошибочным использованием заголовков') end
if needsChangetoSubtitles(args) then table.insert(cats, 'Навигационные шаблоны с использованием устаревших подзаголовков') end
if needsHorizontalLists(args) then table.insert(cats, 'Навигационные шаблоны без горизонтальных списков') end
return cats
end
local function renderTrackingCategories(builder, args)
local title = mw.title.getCurrentTitle()
if title.namespace ~= 10 -- not in template space
or title.subpageText == 'sandbox'
or title.subpageText == 'testcases'
then
return
end
for i, cat in ipairs(getTrackingCategories(args)) do
builder:wikitext('[[Категория:Шаблоны:' .. cat .. ']]')
end
end
--
-- Main navbox tables
--
local function renderMainTable(args, listnums)
local tbl = mw.html.create('table')
:addClass('nowraplinks')
:addClass(args.bodyclass)
if args.title and args.state ~= 'plain' and args.state ~= 'off' then
local isNavboxPage
if args.name then
local curTitle = mw.title.getCurrentTitle()
local navTitle = mw.title.new(args.name, 10)
isNavboxPage = navTitle and mw.title.equals(curTitle, navTitle)
end
tbl
:addClass('mw-collapsible')
:addClass(
args.state ~= 'uncollapsed'
and not isNavboxPage
and 'mw-collapsed'
or nil
)
:addClass(
args.border ~= 'subgroup' and args.border ~= 'none'
and (args.state == 'autocollapse' or not args.state)
and 'navbox-autocollapse'
or nil
)
end
tbl:css('border-spacing', 0)
if args.border == 'subgroup' or args.border == 'none' then
tbl
:addClass('navbox-subgroup')
:cssText(args.bodystyle)
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, args)
renderAboveRow(tbl, args)
for i, listnum in ipairs(listnums) do
local rowArgs = {
group = checkElAliases(args, 'group', listnum),
list = checkElAliases(args, 'list', listnum),
groupstyle = checkElAliases(args, 'groupstyle', listnum),
listclass = checkElAliases(args, 'listclass', listnum),
liststyle = checkElAliases(args, 'liststyle', listnum),
listpadding = checkElAliases(args, 'listpadding', listnum)
}
renderListRow(tbl, args, i, #listnums, rowArgs)
end
renderBelowRow(tbl, args)
return tbl
end
function p._main(args)
if not yesno then
yesno = function (v) return v end
end
local listnums = {}
for k, v in pairs(args) do
local listnum = (
('' .. k):match('^list(%d+)$')
or ('' .. k):match('^список(%d+)$')
or ('' .. k):match('^value(%d+)$')
)
if listnum then
table.insert(listnums, tonumber(listnum) )
end
end
table.sort(listnums)
args.border = mw.text.trim(args.border or args[1] or '')
for argname, aliasesList in pairs(commonAliases) do
args[argname] = checkAliases(args, aliasesList)
end
args.navbar = yesno(args.navbar)
args.orphan = yesno(args.orphan)
args.tracking = yesno(args.tracking)
args.navigation = yesno(args.navigation)
args.state = (args.state == 'expanded') and 'uncollapsed' or args.state
args.border = (args.border == 'child') and 'subgroup' or args.border
-- render the main body of the navbox
local tbl = renderMainTable(args, listnums)
-- render the appropriate wrapper around the navbox, depending on the border param
local res = mw.html.create()
if args.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>') -- 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
local nav = res:tag('div')
:attr('role', 'navigation')
:node(tbl)
if args.border ~= 'none' then
nav:addClass('navbox')
nav:cssText(args.bodystyle)
end
if args.title then
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title))
else
nav:attr('aria-label', 'Навигационный шаблон')
end
if args.name and args.name ~= '-' then
nav:attr('data-name', args.name)
end
if args.navigation == true then
nav:attr('data-navboxnavigation', '1')
elseif args.navigation == false then
nav:attr('data-navboxnavigation', '0')
end
end
renderTrackingCategories(res, args)
return striped(tostring(res), args)
end
function p.main(frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
if not yesno then
yesno = require('Module:Yesno')
end
args = getArgs(frame, {
wrappers = {
'Template:Navbox',
'Template:Navbox/sandbox'
}
})
-- Read the arguments in the order they'll be output in, to make references
-- number in the right order.
do
local _
_ = checkAliases(args, commonAliases.title)
_ = checkAliases(args, commonAliases.above)
for i = 1, 23 do
_ = checkAliases(args, elementAliases.group, i)
_ = checkAliases(args, elementAliases.list, i)
end
_ = checkAliases(args, commonAliases.below)
end
return p._main(args)
end
return p