Module:Navbox

From wiki
Revision as of 03:54, 14 October 2024 by 1>Shizhao (自Module:Navbox/sandbox2合并Dabao qian版本84454438)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

-- -- This module implements {{#invoke:Navbox|navbox}} --

local p = {}

local getArgs -- lazily initialized

local args 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 lists = { plainlist_t = { patterns = { '^plainlist$', '%splainlist$', '^plainlist%s', '%splainlist%s' }, found = false, styles = 'Plainlist/styles.css' }, hlist_t = { patterns = { '^hlist$', '%shlist$', '^hlist%s', '%shlist%s' }, found = false, styles = 'Hlist/styles.css' } }

local function has_list_class(args_to_check) for _, list in pairs(lists) do if not list.found then for _, arg in pairs(args_to_check) do for _, pattern in ipairs(list.patterns) do if mw.ustring.find(arg or , pattern) then list.found = true break end end if list.found then break end end end end end

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 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 has_collapsible_toggle() return args.state ~= 'off' and args.state ~= 'plain' end

local function has_navbar() return args.navbar ~= 'off' and args.navbar ~= 'plain' and ( args.name or ( mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', ) ~= 'Template:Navbox' and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', ) ~= 'Template:Navbox subgroup' ) ) end

-- extract text color from css, which is the only permitted inline CSS for the navbar local function extract_color(css_str) -- return nil because navbar takes its argument into mw.html which handles -- nil gracefully, removing the associated style attribute return mw.ustring.match(';' .. css_str .. ';', '.*;%s*([Cc][Oo][Ll][Oo][Rr]%s*:%s*.-)%s*;') or nil end

local function renderNavBar(titleCell)

if has_navbar() then local navbar = require('Module:Navbar')._navbar titleCell:wikitext(navbar{ args.name, mini = 1, fontstyle = extract_color( (args.basestyle or ) .. ';' .. (args.titlestyle or ) ) }) end

end

-- -- Title row -- local function renderTitleRow(tbl) if not args.title then return end

has_list_class({args.titleclass})

local titleRow = tbl:tag('tr')

local titleCell = titleRow:tag('th'):attr('scope', 'col')

if has_collapsible_toggle() then titleCell :addClass('collapsible-title') end

local titleColspan = 2 if args.imageleft then titleColspan = titleColspan + 1 end if args.image 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', '110%') :css('margin', '0 5em') :wikitext(addNewline(args.title)) end

-- -- Above/Below rows --

local function getAboveBelowColspan() 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) if not args.above then return end

has_list_class({ args.aboveclass })

tbl:tag('tr') :tag('td') :addClass('navbox-abovebelow') :addClass(args.aboveclass) :cssText(args.basestyle) :cssText(args.abovestyle) :attr('colspan', getAboveBelowColspan()) :tag('div') -- id for aria-labelledby attribute, if no title :attr('id', (not args.title) and mw.uri.anchorEncode(args.above) or nil) :wikitext(addNewline(args.above)) end

local function renderBelowRow(tbl) if not args.below then return end

has_list_class({ args.belowclass })

tbl:tag('tr') :tag('td') :addClass('navbox-abovebelow') :addClass(args.belowclass) :cssText(args.basestyle) :cssText(args.belowstyle) :attr('colspan', getAboveBelowColspan()) :tag('div') :wikitext(addNewline(args.below)) end

-- -- List rows -- local function renderListRow(tbl, listnum) local row = tbl:tag('tr')

if listnum == 1 and args.imageleft then has_list_class({ args.imageclass })

row :tag('td') :addClass('noviewer') :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(addNewline(args.imageleft)) end

if args['group' .. listnum] then has_list_class({ args.groupclass })

local groupCell = row:tag('th')

-- 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 groupCell :attr('id', mw.uri.anchorEncode(args.group1)) end

groupCell :attr('scope', 'row') :addClass('navbox-group') :addClass(args.groupclass) :cssText(args.basestyle) :css('width', args.groupwidth or '1%') -- If groupwidth not specified, minimize width

groupCell :cssText(args.groupstyle) :cssText(args['group' .. listnum .. 'style']) :wikitext(args['group' .. listnum]) end

has_list_class({ args.listclass, args['list' .. listnum .. 'class'] })

local listCell = row:tag('td')

if args['group' .. listnum] then listCell :addClass('navbox-list-with-group') else listCell:attr('colspan', 2) end

if not args.groupwidth then listCell:css('width', '100%') end

local rowstyle -- usually nil so cssText(rowstyle) usually adds nothing if listnum % 2 == 1 then rowstyle = args.oddstyle else rowstyle = args.evenstyle end

local listText = args['list' .. listnum] local oddEven = ODD_EVEN_MARKER

if listText:sub(1, 12) == '<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(rowstyle) :cssText(args['list' .. listnum .. 'style']) :addClass('navbox-list') :addClass('navbox-' .. oddEven) :addClass(args.listclass) :addClass(args['list' .. listnum .. 'class']) :tag('div') :css('padding', (listnum == 1 and args.list1padding) or args.listpadding or '0em 0.25em') :wikitext(addNewline(listText))

if listnum == 1 and args.image then has_list_class({ args.imageclass })

row :tag('td') :addClass('noviewer') :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(addNewline(args.image)) end end

-- -- Tracking categories --

local function needsHorizontalLists() if border == 'subgroup' or args.tracking == 'no' then return false end

return not lists.hlist_t.found and not lists.plainlist_t.found end

local function hasBackgroundColors() 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 argNameAndRealTitleAreDifferent() if border == 'subgroup' or args.tracking == 'no' then return false end

if has_navbar() and args.name ~= mw.title.getCurrentTitle().text then return true end

return false end

local function getTrackingCategories() local cats = {} if needsHorizontalLists() then table.insert(cats, '没有使用水平列表的导航框') end if hasBackgroundColors() then table.insert(cats, '使用背景颜色的导航框') end if argNameAndRealTitleAreDifferent() then table.insert(cats, 'name參數和實際不同的導航框') 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 == 'sandbox' or subpage == 'testcases' then return end

for _, cat in ipairs(getTrackingCategories()) do builder:wikitext() end end

-- -- Load the templatestyles for the navbox -- local function loadTemplateStyles(hiding_templatestyles) local frame = mw.getCurrentFrame()

local hlist_templatestyles = if lists.hlist_t.found then hlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.hlist_t.styles } } end

-- a second workaround for phab:T303378 -- when that issue is fixed, we can actually use has_navbar not to emit the -- tag here if we want if has_navbar() and hlist_templatestyles == then hlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.hlist_t.styles } } end

local plainlist_templatestyles = if lists.plainlist_t.found then plainlist_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = lists.plainlist_t.styles } } end

local base_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = 'Module:Navbox/styles.css' } }

local templatestyles = if args['templatestyles'] then templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['templatestyles'] } } end

local child_templatestyles = if args['child templatestyles'] then child_templatestyles = frame:extensionTag{ name = 'templatestyles', args = { src = args['child templatestyles'] } } end

return mw.html.create('div') :addClass('navbox-styles') :wikitext( -- hlist -> plainlist -> base is best-effort to preserve old Common.css ordering. -- this ordering is not a guarantee because the rows of interest invoking -- each class may not be on a specific page hlist_templatestyles .. plainlist_templatestyles .. base_templatestyles .. templatestyles .. child_templatestyles .. table.concat(hiding_templatestyles) ) :done() end

-- work around phab:T303378 -- for each arg: find all the templatestyles strip markers, insert them into a -- table. then remove all templatestyles markers from the arg local function move_hiding_templatestyles(args) local gfind = string.gfind local gsub = string.gsub local templatestyles_markers = {} local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)' for k, arg in pairs(args) do for marker in gfind(arg, strip_marker_pattern) do table.insert(templatestyles_markers, marker) end args[k] = gsub(arg, strip_marker_pattern, ) end return templatestyles_markers end

-- -- Main navbox tables -- local function renderMainTable() local tbl = mw.html.create('table') :addClass('nowraplinks') :addClass(args.bodyclass)

has_list_class({ args.bodyclass })

if args.title and has_collapsible_toggle() then if args.state == 'collapsed' then args.state = 'mw-collapsed' end tbl :addClass('mw-collapsible') :addClass(args.state or 'autocollapse') end

tbl:css('border-spacing', 0) 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) for i, listnum in ipairs(listnums) do renderListRow(tbl, listnum) end renderBelowRow(tbl)

return tbl end

function p._navbox(navboxArgs) args = navboxArgs local hiding_templatestyles = move_hiding_templatestyles(args) listnums = {}

for k, v in pairs(args) do local listnum = ( .. k):match('^list(%d+)$') if listnum then table.insert(listnums, tonumber(listnum)) end end table.sort(listnums)

border = mw.text.trim(args.border or args[1] or ) if border == 'child' then border = 'subgroup' end

-- 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 res:node(loadTemplateStyles(hiding_templatestyles)) local nav = res:tag('div') :attr('role', 'navigation') :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 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 to avoid the -- padding being applied, and at the end add a

to balance out the parent's

res

:wikitext('') -- mw.html 未支持 unclosed

:node(tbl)

:wikitext('

') -- mw.html 未支持 unclosed

else res:node(loadTemplateStyles(hiding_templatestyles))

has_list_class({ args.navboxclass })

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

if (args.nocat or 'false'):lower() == 'false' then renderTrackingCategories(res) end return striped(tostring(res)) end

function p.navbox(frame) if not getArgs then getArgs = require('Module:Arguments').getArgs 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

-- Read the arguments in the order they'll be output in, to make references number in the right order. local _ _ = args.title _ = args.above for i = 1, 35 do _ = args["group" .. tostring(i)] _ = args["list" .. tostring(i)] end _ = args.below

return p._navbox(args) end

return p