--
-- Этот модуль реализует This module will implement {{Навигационная таблицаNavbox}}.-- Основной объём кода заимствован из английского Module:Navbox.
--
local p = {}
local getArgs -- lazily initializedHtmlBuilder = require('Module:HtmlBuilder')local Navbar = require('Module:Navbar')
local args
local frame
local tableRowAdded = false
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 maintitlelocal namelocal navbarlocal abovelocal imagelocal below local titlestylelocal groupstylelocal bodystylelocal basestylelocal liststylelocal oddstylelocal evenstylelocal evenoddARGlocal abovestylelocal belowstylelocal imageleftlocal imageleftstylelocal style local groupwidthlocal listpadding local bodyclasslocal titleclasslocal aboveclasslocal belowclasslocal groupclasslocal listclass local imageclass local function stripedtrim(wikitexts) -- Return wikitext with markers replaced for odd/even striping. -- Child return (subgroup) navboxes are flagged with a category that is removed -- by parent navboxesmw. The result is that the category shows all pages -- where a child navbox is not contained in a parent navboxustring. local orphanCat = '[[Категория:Навигационные шаблоны без родителя]]' if border == 'subgroup' and args.orphan ~= 'yes' then -- No change; striping occurs in outermost navbox. return wikitext .. orphanCat end local firstgsub(s, 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 "^%s*(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('([)%[%]])'s*$", '%%"%1'") return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer)) -- () omits gsub count
end
local function addNewlineaddTableRow(stbl) -- If any other rows have already been added, then we add a 2px gutter row. if s:matchtableRowAdded then tbl .tag('^[*:;#]tr') or s:match .css('^{|height', '2px') then return '\n' .. s . .tag('\ntd') end else tableRowAdded = true return s endtbl.tag('tr')
end
local function renderNavBar(titleCell)
if navbar ~= 'off' and navbar ~= 'plain' and not (not name and mw.getCurrentFrame():getParent():getTitle():gsub('/песочница$', '') == 'Шаблон:Навигационная таблица') then
-- Check color contrast of the gear icon
local styleratio = require('Module:Color contrast')._styleratio
local gearColor = ''
local contrastStyle = titlestyle or basestyle
local gearStyleBlack = (contrastStyle and mw.text.unstripNoWiki(contrastStyle) .. '; color:#666;' or '')
local gearStyleWhite = (contrastStyle and mw.text.unstripNoWiki(contrastStyle) .. '; color:#fff;' or '')
if styleratio{gearStyleBlack} < styleratio{gearStyleWhite} then
gearColor = ' white'
end
--- Gear creation
titleCell
:tag('span')
:css('float', 'left')
:css('text-align', 'left')
:css('width', '5em')
:css('margin-right', '0.5em')
:wikitext('[[Файл:Wikipedia interwiki section gear icon' .. gearColor .. '.svg|14px|Просмотр этого шаблона|link=Шаблон:' .. name .. '|alt=⛭]]')
end
end
--
-- Title row
--
local function renderTitleRow(tbl) if not maintitle args.title then return end local titleRow = addTableRow(tbl) 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', 'col') 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') .addClass(args.titleclass) .css('font-size', '110%') .newline() .wikitext(args.title)end
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 -- or right to keep the title centered. local titleRow spacerSide = tbl:tag('tr')nil
if args.titlegroup navbar == 'off' then titleRow -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's :tag( -- also no show/hide link, then we need a spacer on the right to achieve the left shift. if args.state == 'plain' then spacerSide = 'thright')end :attr elseif args.navbar == 'plain' or args.navbar == 'off' or (not args.name and (border == 'subgroup' or border == 'scopechild', or border == 'rownone')) then :addClass('navbox --group')No navbar. Need a spacer on the left to balance out the width of the show/hide link. :addClass( if args.titlegroupclass)state ~= 'plain' then spacerSide = 'left' end :cssText(basestyle) else :cssText -- Will render navbar (groupstyleor error message). If there's no show/hide link, need a spacer on the right :cssText(args -- to balance out the width of the navbar.titlegroupstyle) :wikitext( if args.titlegroup) state == 'plain' then spacerSide = 'right' end
local titleCell = titleRow:tag.wikitext('th'):attrNavbar.navbar('scope', 'col'){ if args.titlegroup thenname, titleCell mini = 1, :css fontstyle = (args.basestyle or 'border-left', ) .. '2px solid #fdfdfd;') :css.. (args.titlestyle or 'width', ) .. '100%;background:none transparent;border:none;' })) end local titleColspan = 2 -- Render the spacer div. if imageleft spacerSide then titleColspan = titleColspan + 1 end if image then titleColspan = titleColspan + 1 end titleCell if args .titlegroup then titleColspan = titleColspan - 1 end titleCell :cssTexttag(basestyle'span') :cssText .css(titlestyle'float', spacerSide) :addClass .css('navbox-titlewidth', '6em') :attr .wikitext('colspan ', titleColspan) endend
renderNavBar(titleCell)
titleCell
:tag('div')
:attr('id', mw.uri.anchorEncode(maintitle))
:addClass(titleclass)
:css('font-size', '114%')
:css('margin', '0 5em')
:wikitext(addNewline(maintitle))
end
--
-- Above/Below rows
--
local function getAboveBelowColspanrenderAboveRow(tbl) local ret = 2 if imageleft not args.above then ret = ret + 1 return end if image then ret = ret + 1 end addTableRow(tbl) .tag('td') .addClass('navbox-abovebelow') .addClass(args.aboveclass) .cssText(args.basestyle) .cssText(args.abovestyle) .attr('colspan', getAboveBelowColspan()) .tag('div') .newline() return ret .wikitext(args.above)
end
local function renderAboveRowrenderBelowRow(tbl) if not above args.below then return end addTableRow(tbl:tag('tr') : .tag('td') : .addClass('navbox-abovebelow') : .addClass(aboveclassargs.belowclass) : .cssText(args.basestyle) : .cssText(abovestyleargs.belowstyle) : .attr('colspan', getAboveBelowColspan()) : .tag('div') : .newline() .wikitext(addNewline(above)args.below)
end
local function renderBelowRowgetAboveBelowColspan(tbl) local ret = 2 if not below args.imageleft then return ret = ret + 1 end if args.image then ret = ret + 1 end tbl:tag('tr') :tag('td') :addClass('navbox-abovebelow') :addClass(belowclass) :cssText(basestyle) :cssText(belowstyle) :attr('colspan', getAboveBelowColspan()) :tag('div') :wikitext(addNewline(below)) return ret
end
--
-- List rows
--
function renderListRow(tbl, listnum)
local row = addTableRow(tbl)
if listnum == 1 and args.imageleft then
row
.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')
.newline()
.wikitext(args.imageleft)
end
if args['group' .. listnum] then
local groupCell = row.tag('th')
groupCell
.attr('scope', 'row')
.addClass('navbox-group')
.addClass(args.groupclass)
.cssText(args.basestyle)
if args.groupwidth then
groupCell.css('width', args.groupwidth)
end
groupCell
.cssText(args.groupstyle)
.cssText(args['group' .. listnum .. 'style'])
.wikitext(args['group' .. listnum])
end
local listCell = row.tag('td')
if args['group' .. listnum] 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 function haveSubgroupsisOdd = (listnum % 2)== 1 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 for i if isOdd then evenOdd = args.evenodd or 'odd' else evenOdd = 1args.evenodd or 'even' end end listCell .css('padding', 23 do'0px') .cssText(args.liststyle) if .cssText(rowstyle) .cssText(args['grouplist' .. ilistnum .. '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' ) .newline() . i] or wikitext(args['группаlist' .. ilistnum]) if listnum == 1 and args.image then row .tag('td') .addClass('navbox-image') .addClass(args[.imageclass) .css('width', 'list0%' ) .css('padding', '0px 0px 0px 2px') . i] or cssText(args[.imagestyle) .attr('списокrowspan' , 2 * #listnums - 1) .tag('div') . i]newline() then return true end .wikitext(args.image) end return false
end
local function renderListRow(tbl, index, listnum)
local row = tbl:tag('tr')
if index == 1 and imageleft then
row
:tag('td')
:addClass('navbox-image')
:addClass(imageclass)
:css('width', '1px')
:css('padding', '0px 7px 0px 0px')
:cssText(imageleftstyle)
:attr('rowspan', #listnums)
:tag('div')
:wikitext(addNewline(imageleft))
end
if (args['group' .. listnum] or args['заголовок' .. listnum] or args['группа' .. listnum]) then
local groupCell = row:tag('th')
groupCell
:attr('scope', 'row')
:addClass('navbox-group')
:addClass(groupclass)
:cssText(basestyle)
:css('width', args.groupwidth or '1px') -- If groupwidth not specified, minimize width
-- заголовки без списков - для обратной совместимости, только в нашем разделе
if not (args['list' .. listnum] or args['список' .. listnum]) then
groupCell
:css('text-align', 'center')
if haveSubgroups() then
groupCell:attr('colspan', 2)
end
end
groupCell
:cssText(groupstyle)
:cssText(args['group' .. listnum .. 'style'] or args['стиль_группы' .. listnum] or args['стиль_заголовка' .. listnum])
:wikitext(args['group' .. listnum] or args['заголовок' .. listnum] or args['группа' .. listnum])
end
if args['list' .. listnum] or args['список' .. listnum] then -- проверка на наличие списков, иначе временный наш безсписочный функционал не поддерживается
local listCell = row:tag('td')
if (args['group' .. listnum] or args['заголовок' .. listnum] or args['группа' .. listnum]) then
listCell
:css('text-align', 'left')
:css('border-left-width', '2px')
:css('border-left-style', 'solid')
else
if haveSubgroups() then
listCell
:attr('colspan', 2)
end
end
if not groupwidth then
listCell:css('width', '100%')
end
local rowstyle -- usually nil so cssText(rowstyle) usually adds nothing
if index % 2 == 1 then
rowstyle = oddstyle
else
rowstyle = evenstyle
end
local listText = args['list' .. listnum] or args['список' .. listnum]
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(liststyle)
:cssText(rowstyle)
:cssText(args['list' .. listnum .. 'style'] or args['стиль_списка' .. listnum])
:addClass('navbox-list')
:addClass('navbox-' .. oddEven)
:addClass(listclass)
:tag('div')
:css('padding', (index == 1 and args.list1padding) or listpadding or '0em 0.25em')
:wikitext(addNewline(listText))
end
if index == 1 and image then
row
:tag('td')
:addClass('navbox-image')
:addClass(imageclass)
:css('width', '1px')
:css('padding', '0px 0px 0px 7px')
:cssText(imagestyle)
:attr('rowspan', #listnums)
:tag('div')
:wikitext(addNewline(image))
end
end
--
-- Tracking categories
--
function renderTrackingCategories(builder) local function needsChangetoSubgroupsframe = mw.getCurrentFrame() for i if not frame then return end local s = frame:preprocess('{{#ifeq:{{NAMESPACE}}|{{ns:10}}|1, 23 do|0}}{{SUBPAGENAME}}') if mw.ustring.sub(args[s, 1, 1) == 'group0' then return end -- not in template space local subpage = mw.ustring.lower(mw. i] ustring.sub(s, 2)) if subpage == 'doc' or args[subpage == 'заголовокsandbox' .. i] or args[subpage == 'группаtestcases' .. then return end for i], cat in ipairs(getTrackingCategories()) and not do builder.wikitext(args'['list[Category:' .. i] or args['список' cat .. i']]') then return true end end return false
end
local function needsHorizontalListsgetTrackingCategories() if border == 'subgroup' or args.tracking == 'no' then return false end local listClasses cats = {} ['plainlist'] = true if needsHorizontalLists() then table.insert(cats, ['hlistNavigational boxes without horizontal lists'] = true, ['hlist hnum'] = true,) end ['hlist hwrap'] = true if hasBackgroundColors() then table.insert(cats, ['hlist vcardNavboxes using background colours'] = true, ['vcard hlist'] = true,) end ['hlist vevent'] = true, ['hlist hlist-items-nowrap'] = true, ['hlist-items-nowrap'] = true, } return not (listClasses[listclass] or listClasses[bodyclass])cats
end
-- local function hasBackgroundColorsneedsHorizontalLists()-- if border == 'child' or border == 'subgroup' or args.tracking == 'no' then return mw.ustring.match(titlestyle or false end local listClasses = {'plainlist', 'hlist','backgroundhlist hnum') or mw.ustring.match(groupstyle or , 'hlist hwrap','backgroundhlist vcard') or mw.ustring.match(basestyle or , 'vcard hlist','backgroundhlist vevent'} for i, cls in ipairs(listClasses)do if args.listclass == cls or args.bodyclass == cls then return false end-- end
local function isIllegible() local styleratio = require('Module:Color contrast')._styleratio for key, style in pairs(args) do if tostring(key):match("style$") or tostring(key):match("^стиль") then if styleratio{mw.text.unstripNoWiki(style)} < 4.5 then return true end end end return false
end
local function getTrackingCategorieshasBackgroundColors() local cats = {} if needsChangetoSubgroups() then table return args.insert(cats, 'Навигационные шаблоны с ошибочным использованием заголовков') end if needsHorizontalLists() then tabletitlestyle or args.insert(cats, 'Навигационные шаблоны без горизонтальных списков') end if isIllegible() then table.insert(cats, 'Потенциально нечитаемые навигационные шаблоны') end return catsgroupstyle
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 == 'песочница' or subpage == 'тесты' then return end
for i, cat in ipairs(getTrackingCategories()) do
builder:wikitext('[[Категория:' .. cat .. ']]')
end
end
--
-- Main navbox tables
--
local function renderMainTable() local tbl = mw.htmlHtmlBuilder.create('table') : .attr('cellspacing', 0) .addClass('nowraplinks') : .addClass(args.bodyclass) if maintitle args.title and (args.state ~= 'plain' and args.state ~= 'off') then tbl : .addClass('collapsible') : .addClass(args.state or 'autocollapse') end tbl:.css('border-spacing', 0) if border == 'subgroup' or border == 'child' or border == 'none' then tbl : .addClass('navbox-subgroup') : .cssText(args.bodystyle) : .cssText(args.style) else -- regular navbox 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, i, listnum) end renderBelowRow(tbl) return tbl
end
function p._navbox(navboxArgs)
args = navboxArgs for k, v in pairs(args) do local listnum = ('' .. k):match('^list(%d+)$') or ('' .. k):match('^список(%d+)$') if listnum then table.insert(listnums, tonumber(listnum)) end end -- заголовки без списков - для обратной совместимости, только в нашем разделе for k, v in pairs(args) do local double = false local groupnum = ('' .. k):match('^заголовок(%d+)$') --group не нужен, так как в английском шаблоне эта фукнциональность не поддерживается if groupnum then for k2, v2 in pairs(listnums) do if tonumber(groupnum) == v2 then double = true break end end if not double then table.insert(listnums, tonumber(groupnum)) end --добавляем только номера заголовков, для которых нет списков end end table.sort(listnums) border = mw.text.trim(args.border or args[1] or '') if border == 'child' then border = 'subgroup' end maintitle = args.title or args['заголовок'] navbar = args.navbar or args['ссылка_на_просмотр'] name = args.name or args['имя'] above = args.above or args['вверху'] image = args.image or args['изображение'] imagestyle = args.imagestyle or args['стиль_изображения'] imageleft = args.imageleft or args['изображение2'] or args['изображение_слева'] imageleftstyle = args.imageleftstyle or args.imagestyle2 or args['стиль_изображения_слева'] below = args.below or args['внизу'] titlestyle = args.titlestyle or args['стиль_основного_заголовка'] or args['стиль_заголовка'] groupstyle = args.groupstyle or args['стиль_заголовков'] or args['стиль_групп'] bodystyle = args.bodystyle or args['стиль_тела'] basestyle = args.basestyle or args['стиль_базовый'] or args['стиль'] style = args.style liststyle = args.liststyle or args['стиль_списков'] oddstyle = args.oddstyle or args['стиль_нечётных'] or args['стиль_нечетных'] evenstyle = args.evenstyle or args['стиль_чётных'] or args['стиль_четных'] abovestyle = args.abovestyle or args['стиль_вверху'] belowstyle = args.belowstyle or args['стиль_внизу'] evenoddARG = args.evenodd or args['чётные_нечётные'] or args['четные_нечетные'] groupwidth = args.groupwidth or args['ширина_групп'] listpadding = args.listpadding or args['отступ_списков'] bodyclass = args.bodyclass or args['класс_тела'] titleclass = args.titleclass or args['класс_заголовка'] aboveclass =args.aboveclass or args['класс_вверху'] belowclass = args.belowclass or args['класс_внизу'] groupclass = args.groupclass or args['класс_групп'] listclass = args.listclass or args['класс_списков'] imageclass = args.imageclass or args['класс_изображения'] -- render the main body of the navbox local tbl = renderMainTable()
-- render the appropriate wrapper around main body of the navbox, depending on the border param local res = mw.html.create() if border == 'none' then local nav = res:tag('div') :attr('role', 'navigation') :node(tbl) if maintitle then nav:attr('aria-labelledby', mw.uri.anchorEncode(maintitle)) else nav:attr('aria-label', 'Навигационный шаблон') 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 :wikitextrenderMainTable('</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') :addClass('navbox') :cssText(bodystyle) :cssText(style) :css('padding', '3px') :node(tbl) if maintitle then nav:attr('aria-labelledby', mw.uri.anchorEncode(maintitle)) else nav:attr('aria-label', 'Навигационный шаблон') end end
-- render the appropriate wrapper around the navbox, depending on the border param local res = HtmlBuilder.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 .tag('/div', {unclosed = true}) .done() .node(tbl) .tag('div', {unclosed = true}) else res .tag('table') .attr('cellspacing', 0) .addClass('navbox') .css('border-spacing', 0) .cssText(args.bodystyle) .cssText(args.style) .tag('tr') .tag('td') .css('padding', '2px') .node(tbl) end renderTrackingCategories(res) return striped(tostring(res))
end
function p.navbox(frame)
if not getArgs then -- ParserFunctions considers the empty string to be false, so to preserve the previous -- behavior of {{navbox}}, change any empty arguments to nil, so Lua will consider getArgs = require('Module:Arguments') -- them false too.getArgs end local args = getArgs(frame, {wrappers } local parent_args = {'Шаблонframe:Навигационная таблица', 'Шаблон:Навигационная таблица/песочница'}}getParent().args;
-- Read the arguments in the order they'll be output in, to make references number in the right Out of orderparsing bug. local _temp; _ temp = maintitleparent_args.title; _ temp = parent_args.above; for i = 1, 23 20 do _ temp = argsparent_args["group" .. tostring(i)] and args; temp = parent_args["заголовокlist" .. tostring(i)] and args["группа" ; end temp = parent_args.. tostringbelow; for k, v in pairs(iparent_args)]do _ if v ~= '' then args["list" .. tostring(i)] and args["список" .. tostring(i)k]= v end _ = below end return p._navbox(args)
end
return p