Бюрократи, Чек'юзери, Боти для структурованих обговорень, Адміністратори інтерфейсу, Модератори, Адміністратори (Семантична MediaWiki), Куратори (Семантична MediaWiki), Приховувачі, Адміністратори
1655
редагувань
Tviggy (обговорення | внесок) (Створена сторінка: -------------------------------------------------------------------- --<pre> Navbox Module -- -- * Fully CSS styled (inline styles possible but not default) --...) |
Tviggy (обговорення | внесок) м |
||
Рядок 1: | Рядок 1: | ||
-- | -- | ||
-- | -- Реализует {{Навигационная таблица}} и {{Подгруппы навигационной таблицы}}. | ||
-- | -- Основной объём кода заимствован из английского Module:Navbox. | ||
-- | -- | ||
local p = {} | local p = {} | ||
local | local getArgs -- lazily initialized | ||
local | local yesno -- lazily initialized | ||
local args | |||
local | local border | ||
local | local listnums = {} | ||
local | local ODD_EVEN_MARKER = '\127_ODDEVEN_\127' | ||
local | local RESTART_MARKER = '\127_ODDEVEN0_\127' | ||
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127' | |||
local | |||
local maintitle | |||
local | local name | ||
local | local navigation | ||
local navbar | |||
local above | |||
local image | |||
local below | |||
local titlestyle | |||
local groupstyle | |||
local bodystyle | |||
local basestyle | |||
local liststyle | |||
local evenodd_VARIABLE | |||
local abovestyle | |||
local belowstyle | |||
local imageleft | |||
local imageleftstyle | |||
local | local style | ||
local groupwidth | |||
local listpadding | |||
local bodyclass | |||
local titleclass | |||
local aboveclass | |||
local belowclass | |||
local groupclass | |||
local listclass | |||
local function | local imageclass | ||
local | local function striped(wikitext) | ||
-- 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 border == 'subgroup' and args.orphan ~= 'yes' then | |||
-- No change; striping occurs in outermost navbox. | |||
return wikitext .. orphanCat | |||
end | end | ||
local first, second = 'odd', 'even' | |||
if evenodd_VARIABLE then | |||
if evenodd_VARIABLE == 'swap' then | |||
first, second = second, first | |||
if | |||
if | |||
else | else | ||
first = evenodd_VARIABLE | |||
second = first | |||
end | end | ||
end | end | ||
local changer | |||
if first == second then | |||
if | 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 | end | ||
local regex = orphanCat:gsub('([%[%]])', '%%%1') | |||
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer)) -- () omits gsub count | |||
end | end | ||
local function addNewline(s) | |||
if s:match('^[*:;#]') or s:match('^{|') then | |||
return '\n' .. s ..'\n' | |||
local function | |||
if | |||
else | else | ||
return s | |||
end | end | ||
end | |||
if | |||
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') | |||
:addClass('navbox-gear') | |||
: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 | ||
end | end | ||
--- | -- | ||
local function | -- Title row | ||
if not | -- | ||
local function renderTitleRow(tbl) | |||
local | if not maintitle 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(basestyle) | |||
:cssText(groupstyle) | |||
:cssText(args.titlegroupstyle) | |||
:wikitext(args.titlegroup) | |||
end | end | ||
local titleCell = titleRow:tag('th'):attr('scope', 'col') | |||
if args.titlegroup then | |||
titleCell | |||
if | :css('border-left', '2px solid #fdfdfd') | ||
:css('width', '100%') | |||
end | end | ||
local titleColspan = 2 | |||
if imageleft then titleColspan = titleColspan + 1 end | |||
if image then titleColspan = titleColspan + 1 end | |||
if args.titlegroup then titleColspan = titleColspan - 1 end | |||
titleCell | |||
:cssText(basestyle) | |||
:cssText(titlestyle) | |||
:addClass('navbox-title') | |||
:attr('colspan', titleColspan) | |||
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 | end | ||
--- | -- | ||
-- Above/Below rows | |||
-- | -- | ||
local function getAboveBelowColspan() | |||
local function | local ret = 2 | ||
if | if imageleft then ret = ret + 1 end | ||
if image then ret = ret + 1 end | |||
return ret | |||
end | |||
local function renderAboveRow(tbl) | |||
if not above then return end | |||
tbl:tag('tr') | |||
:tag('td') | |||
:addClass('navbox-abovebelow') | |||
:addClass(aboveclass) | |||
:cssText(basestyle) | |||
:cssText(abovestyle) | |||
:attr('colspan', getAboveBelowColspan()) | |||
:tag('div') | |||
:wikitext(addNewline(above)) | |||
end | |||
local function renderBelowRow(tbl) | |||
if not below then return end | |||
tbl:tag('tr') | |||
:tag('td') | |||
:addClass('navbox-abovebelow') | |||
:addClass(belowclass) | |||
:cssText(basestyle) | |||
:cssText(belowstyle) | |||
:attr('colspan', getAboveBelowColspan()) | |||
:tag('div') | |||
:wikitext(addNewline(below)) | |||
end | end | ||
--- | -- | ||
-- List rows | |||
-- | -- | ||
local function | local function haveSubgroups() | ||
if | for i = 1, 23 do | ||
if (args['group' .. i] or args['заголовок' .. i] or args['группа' .. i]) and (args['list' .. i] or args['список' .. i]) then | |||
return true | |||
end | end | ||
end | end | ||
return false | |||
if | 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 | end | ||
if args. | 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', groupwidth or '1px') -- If groupwidth not specified, minimize width | |||
-- | 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 | end | ||
local | 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 | else | ||
if haveSubgroups() then | |||
listCell | |||
:attr('colspan', 2) | |||
end | |||
end | end | ||
if not groupwidth then | |||
if | listCell:css('width', '100%') | ||
end | 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 | end | ||
listCell | |||
:css('padding', '0px') | |||
:cssText(liststyle) | |||
:cssText(args['list' .. listnum .. 'style'] or args['стиль_списка' .. listnum]) | |||
if | :addClass('navbox-list') | ||
:addClass('navbox-' .. oddEven) | |||
:addClass(listclass) | |||
:tag('div') | |||
:css('padding', args['list' .. listnum .. 'padding'] or listpadding or '0em 0.25em') | |||
:wikitext(addNewline(listText)) | |||
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 | ||
end | |||
-- | |||
if not | -- Tracking categories | ||
-- | |||
local function needsChangetoSubgroups() | |||
for i = 1, 23 do | |||
if (args['group' .. i] or args['заголовок' .. i] or args['группа' .. i]) and not (args['list' .. i] or args['список' .. i]) then | |||
return true | |||
end | end | ||
end | end | ||
return false | |||
end | |||
local function needsHorizontalLists() | |||
if border == 'subgroup' or args.tracking == 'no' then | |||
return false | |||
end | |||
local listClasses = { | |||
['plainlist'] = true, ['hlist'] = true, ['hlist hnum'] = true, | |||
['hlist hwrap'] = true, ['hlist vcard'] = true, ['vcard hlist'] = true, | |||
['hlist vevent'] = true, ['hlist hlist-items-nowrap'] = true, ['hlist-items-nowrap'] = true, | |||
} | |||
return not (listClasses[listclass] or listClasses[bodyclass]) | |||
end | end | ||
--- | -- local function hasBackgroundColors() | ||
local function | -- return mw.ustring.match(titlestyle or '','background') or mw.ustring.match(groupstyle or '','background') or mw.ustring.match(basestyle or '','background') | ||
-- end | |||
for | |||
local function isIllegible() | |||
if | 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 | ||
end | end | ||
return false | |||
end | |||
if | |||
local function getTrackingCategories() | |||
end | local cats = {} | ||
if | if needsChangetoSubgroups() then table.insert(cats, 'Навигационные шаблоны с ошибочным использованием заголовков') end | ||
if needsHorizontalLists() then table.insert(cats, 'Навигационные шаблоны без горизонтальных списков') end | |||
if isIllegible() then table.insert(cats, 'Потенциально нечитаемые навигационные шаблоны') end | |||
return cats | |||
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 | ||
end | end | ||
-- | -- | ||
-- Main navbox tables | |||
-- | |||
-- | -- | ||
local function renderMainTable() | |||
local function | local tbl = mw.html.create('table') | ||
local | :addClass('nowraplinks') | ||
:addClass(bodyclass) | |||
if | |||
if maintitle 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 == 'none' then | |||
tbl | |||
:addClass('navbox-subgroup') | |||
:cssText(bodystyle) | |||
:cssText(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) | |||
for i, listnum in ipairs(listnums) do | |||
renderListRow(tbl, i, listnum) | |||
end | end | ||
renderBelowRow(tbl) | |||
return tbl | |||
end | |||
function p._navbox(navboxArgs) | |||
args = navboxArgs | |||
if not yesno then | |||
yesno = require('Module:Yesno'); | |||
end | end | ||
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 | end | ||
table.sort(listnums) | |||
border = mw.text.trim(args.border or args[1] or '') | |||
if border == 'child' then | |||
border = 'subgroup' | |||
end | end | ||
maintitle = args.title or args['заголовок'] | |||
navbar = args.navbar or args['ссылка_на_просмотр'] | |||
name = args.name or args['имя'] | |||
navigation = yesno(args.navigation 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['стиль_списков'] | |||
abovestyle = args.abovestyle or args['стиль_вверху'] | |||
belowstyle = args.belowstyle or args['стиль_внизу'] | |||
end | evenodd_VARIABLE = 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 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 | |||
if name and name ~= '-' then | |||
nav:attr('data-name', name) | |||
end | |||
if navigation == true then | |||
nav:attr('data-navboxnavigation', '1') | |||
elseif navigation == false then | |||
nav:attr('data-navboxnavigation', '0') | |||
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>') -- 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 | |||
if name and name ~= '-' then | |||
nav:attr('data-name', name) | |||
end | end | ||
if | if navigation == true then | ||
nav:attr('data-navboxnavigation', '1') | |||
elseif navigation == false then | |||
nav:attr('data-navboxnavigation', '0') | |||
end | end | ||
end | end | ||
renderTrackingCategories(res) | |||
return striped(tostring(res)) | |||
end | end | ||
function p.navbox(frame) | |||
if not getArgs then | |||
getArgs = require('Module:Arguments').getArgs | |||
end | |||
if not yesno then | |||
yesno = require('Module:Yesno'); | |||
end | end | ||
args = getArgs(frame, {wrappers = {'Шаблон:Навигационная таблица', 'Шаблон:Подгруппы навигационной таблицы'}}) | |||
if frame.args.border then | |||
-- This allows Template:Navbox_subgroup to use {{#invoke:Navbox|navbox|border=...}}. | |||
if | args.border = frame.args.border | ||
navbox = | |||
end | end | ||
-- Read the arguments in the order they'll be output in, to make references number in the right order. | |||
local _ | |||
_ = maintitle | |||
-- | _ = above | ||
for i = 1, 23 do | |||
_ = args["group" .. tostring(i)] and args["заголовок" .. tostring(i)] and args["группа" .. tostring(i)] | |||
_ = args["list" .. tostring(i)] and args["список" .. tostring(i)] | |||
end | end | ||
_ = below | |||
return p._navbox(args) | |||
end | end | ||
return p | return p |