跳转到内容

Module:Navbox:修订间差异

来自二次元百科
JasonHK留言 | 贡献
导入1个版本
第1行: 第1行:
--
--
-- This module will implement {{Navbox}}
-- This module implements {{Navbox}}
--
--
 
local p = {}
local p = {}
 
local navbar = require('Module:Navbar')._navbar
local getArgs -- lazily initialized
local getArgs -- lazily initialized


local args
local args
local tableRowAdded = false
local border
local border
local listnums = {}
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 trim(s)
local function striped(wikitext)
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
-- 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 = '[[Category:孤立的子导航框]]'
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
end


local function addNewline(s)
local function addNewline(s)
    if s:match('^[*:;#]') or s:match('^{|') then
if s:match('^[*:;#]') or s:match('^{|') then
        return '\n' .. s ..'\n'
return '\n' .. s ..'\n'
    else
else
        return s
return s
    end
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
end


local function addTableRow(tbl)
-- extract text color from css, which is the only permitted inline CSS for the navbar
    -- If any other rows have already been added, then we add a 2px gutter row.
local function extract_color(css_str)
    if tableRowAdded then
-- return nil because navbar takes its argument into mw.html which handles
        tbl
-- nil gracefully, removing the associated style attribute
            :tag('tr')
return mw.ustring.match(';' .. css_str .. ';', '.*;%s*([Cc][Oo][Ll][Oo][Rr]%s*:%s*.-)%s*;') or nil
                :css('height', '2px')
                :tag('td')
                :attr('colspan', 3)
    end
   
    tableRowAdded = true
   
    return tbl:tag('tr')
end
end


local function renderNavBar(titleCell)
local 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 spacerSide = nil


    if args.navbar == 'off' then
if has_navbar() then
        -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's
local navbar = require('Module:Navbar')._navbar
        -- also no show/hide link, then we need a spacer on the right to achieve the left shift.
titleCell:wikitext(navbar{
        if args.state == 'plain' then spacerSide = 'right' end
args.name,
    elseif args.navbar == 'plain' or (not args.name and mw.getCurrentFrame():getParent():getTitle() == 'Template:Navbox' and (border == 'subgroup' or border == 'child' or border == 'none')) then
mini = 1,
        -- No navbar. Need a spacer on the left to balance out the width of the show/hide link.
fontstyle = extract_color(
        if args.state ~= 'plain' then spacerSide = 'left' end
(args.basestyle or '') .. ';' .. (args.titlestyle or '')
    else
)
        -- Will render navbar (or error message). If there's no show/hide link, need a spacer on the right
})
        -- to balance out the width of the navbar.
end
        if args.state == 'plain' then spacerSide = 'right' end


        titleCell:wikitext(navbar{
            args.name,
            mini = 1,
            fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') ..  ';background:none transparent;border:none;'
        })
    end
   
    -- Render the spacer div.
    if spacerSide then
        titleCell
            :tag('span')
                :css('float', spacerSide)
                :css('width', '8em')
                :css('font-size', '80%')
                :css('margin-' .. (spacerSide == 'left' and 'right' or 'left'), '0.5em')
                :wikitext(' ')
    end
end
end


第80行: 第147行:
--
--
local function renderTitleRow(tbl)
local function renderTitleRow(tbl)
    if not args.title then return end
if not args.title then return end
 
    local titleRow = addTableRow(tbl)
has_list_class({args.titleclass})
   
 
    if args.titlegroup then
local titleRow = tbl:tag('tr')
        titleRow
 
            :tag('th')
local titleCell = titleRow:tag('th'):attr('scope', 'col')
                :attr('scope', 'row')
 
                :addClass('navbox-group')
if has_collapsible_toggle() then
                :addClass(args.titlegroupclass)
titleCell
                :cssText(args.basestyle)
:addClass('collapsible-title')
                :cssText(args.groupstyle)
end
                :cssText(args.titlegroupstyle)
 
                :wikitext(args.titlegroup)
local titleColspan = 2
    end
if args.imageleft then titleColspan = titleColspan + 1 end
   
if args.image then titleColspan = titleColspan + 1 end
    local titleCell = titleRow:tag('th'):attr('scope', 'col')
 
           
titleCell
    if args.titlegroup then
:cssText(args.basestyle)
        titleCell
:cssText(args.titlestyle)
            :css('border-left', '2px solid #fdfdfd')
:addClass('navbox-title')
            :css('width', '100%')
:attr('colspan', titleColspan)
    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')
        :addClass(args.titleclass)
        :attr('colspan', titleColspan)
    renderNavBar(titleCell)


    titleCell
renderNavBar(titleCell)
        :tag('div')
 
            :css('font-size', '110%')
titleCell
            :wikitext(addNewline(args.title))
: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
end


第129行: 第187行:


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


local function renderAboveRow(tbl)
local function renderAboveRow(tbl)
    if not args.above then return end
if not args.above then return end


    addTableRow(tbl)
has_list_class({ args.aboveclass })
        :tag('td')
 
            :addClass('navbox-abovebelow')
tbl:tag('tr')
            :addClass(args.aboveclass)
:tag('td')
            :cssText(args.basestyle)
:addClass('navbox-abovebelow')
            :cssText(args.abovestyle)
:addClass(args.aboveclass)
            :attr('colspan', getAboveBelowColspan())
:cssText(args.basestyle)
            :tag('div')
:cssText(args.abovestyle)
                :wikitext(addNewline(args.above))
: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
end


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


    addTableRow(tbl)
has_list_class({ args.belowclass })
        :tag('td')
 
            :addClass('navbox-abovebelow')
tbl:tag('tr')
            :addClass(args.belowclass)
:tag('td')
            :cssText(args.basestyle)
:addClass('navbox-abovebelow')
            :cssText(args.belowstyle)
:addClass(args.belowclass)
            :attr('colspan', getAboveBelowColspan())
:cssText(args.basestyle)
            :tag('div')
:cssText(args.belowstyle)
                :wikitext(addNewline(args.below))
:attr('colspan', getAboveBelowColspan())
:tag('div')
:wikitext(addNewline(args.below))
end
end
 
--
--
--  List rows
--  List rows
--
--
local function renderListRow(tbl, listnum)
local function renderListRow(tbl, listnum)
    local row = addTableRow(tbl)
local row = tbl:tag('tr')
   
 
    if listnum == 1 and args.imageleft then
if listnum == 1 and args.imageleft then
        row
has_list_class({ args.imageclass })
            :tag('td')
 
                :addClass('navbox-image')
row
                :addClass(args.imageclass)
:tag('td')
                :css('width', '0%')
:addClass('noviewer')
                :css('padding', '0px 2px 0px 0px')
:addClass('navbox-image')
                :cssText(args.imageleftstyle)
:addClass(args.imageclass)
                :attr('rowspan', 2 * #listnums - 1)
:css('width', '1px')               -- Minimize width
                :tag('div')
:css('padding', '0px 2px 0px 0px')
                    :wikitext(addNewline(args.imageleft))
:cssText(args.imageleftstyle)
    end
:attr('rowspan', #listnums)
:tag('div')
    if args['group' .. listnum] then
:wikitext(addNewline(args.imageleft))
        local groupCell = row:tag('th')
end
       
 
        groupCell
if args['group' .. listnum] then
              :attr('scope', 'row')
has_list_class({ args.groupclass })
              :addClass('navbox-group')
 
              :addClass(args.groupclass)
local groupCell = row:tag('th')
              :cssText(args.basestyle)
 
             
-- id for aria-labelledby attribute, if lone group with no title or above
        if args.groupwidth then
if listnum == 1 and not (args.title or args.above or args.group2) then
            groupCell:css('width', args.groupwidth)
groupCell
        end
:attr('id', mw.uri.anchorEncode(args.group1))
         
end
        groupCell
 
            :cssText(args.groupstyle)
groupCell
            :cssText(args['group' .. listnum .. 'style'])
:attr('scope', 'row')
            :wikitext(args['group' .. listnum])
:addClass('navbox-group')
    end
:addClass(args.groupclass)
   
:cssText(args.basestyle)
    local listCell = row:tag('td')
: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


    if args['group' .. listnum] then
has_list_class({ args.listclass, args['list' .. listnum .. 'class'] })
        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 isOdd = (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
        if isOdd then evenOdd = args.evenodd or 'odd' else evenOdd = args.evenodd or 'even' end
    end


    listCell
local listCell = row:tag('td')
        :css('padding', '0px')
        :cssText(args.liststyle)
        :cssText(rowstyle)
        :cssText(args['list' .. listnum .. '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')
            :wikitext(addNewline(args['list' .. listnum]))


    if listnum == 1 and args.image then
if args['group' .. listnum] then
        row
listCell
            :tag('td')
:addClass('navbox-list-with-group')
                :addClass('navbox-image')
else
                :addClass(args.imageclass)
listCell:attr('colspan', 2)
                :css('width', '0%')
end
                :css('padding', '0px 0px 0px 2px')
 
                :cssText(args.imagestyle)
if not args.groupwidth then
                :attr('rowspan', 2 * #listnums - 1)
listCell:css('width', '100%')
                :tag('div')
end
                    :wikitext(addNewline(args.image))
 
    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) == '</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(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
end


--
--
第259行: 第336行:


local function needsHorizontalLists()
local function needsHorizontalLists()
    if border == 'child' or border == 'subgroup' or args.tracking == 'no' then return false end
if border == 'subgroup' or args.tracking == 'no' then
   
return false
    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent', 'hlist hlist-pipe', 'hlist hlist-hyphen'}
end
    for i, cls in ipairs(listClasses) do
        if args.listclass == cls or args.bodyclass == cls then
            return false
        end
    end


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


local function hasBackgroundColors()
local function hasBackgroundColors()
    return mw.ustring.match(args.titlestyle or '','background') or mw.ustring.match(args.groupstyle or '','background') or mw.ustring.match(args.basestyle or '','background')
for _, key in ipairs({'titlestyle', 'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
if tostring(args[key]):find('background', 1, true) then
return true
end
end
end
end


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


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


local function getTrackingCategories()
local function getTrackingCategories()
    local cats = {}
local cats = {}
    if needsHorizontalLists() then table.insert(cats, '没有使用水平列表的导航框') end
if needsHorizontalLists() then table.insert(cats, '没有使用水平列表的导航框') end
    if hasBackgroundColors() then table.insert(cats, '使用背景颜色的导航框') end
if hasBackgroundColors() then table.insert(cats, '使用背景颜色的导航框') end
    if argNameAndRealTitleAreDifferent() then table.insert(cats, 'name參數和實際不同的導航框') end
if argNameAndRealTitleAreDifferent() then table.insert(cats, 'name參數和實際不同的導航框') end
    return cats
return cats
end
end


local function renderTrackingCategories(builder)
local function renderTrackingCategories(builder)
    local title = mw.title.getCurrentTitle()
local title = mw.title.getCurrentTitle()
    if title.namespace ~= 10 then return end -- not in template space
if title.namespace ~= 10 then return end -- not in template space
    local subpage = title.subpageText
local subpage = title.subpageText
    if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end
if subpage == 'doc' or subpage == 'sandbox' or subpage == 'testcases' then return end
   
 
    for i, cat in ipairs(getTrackingCategories()) do
for _, cat in ipairs(getTrackingCategories()) do
        builder:wikitext('[[Category:' .. cat .. ']]')  
builder:wikitext('[[Category:' .. cat .. ']]')
    end
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
end


第307行: 第466行:
--
--
local function renderMainTable()
local function renderMainTable()
    local tbl = mw.html.create('table')
local tbl = mw.html.create('table')
        :attr('cellspacing', 0)
:addClass('nowraplinks')
        :addClass('nowraplinks')
:addClass(args.bodyclass)
        :addClass(args.bodyclass)
 
           
has_list_class({ args.bodyclass })
    if args.title and (args.state ~= 'plain' and args.state ~= 'off') then
 
        tbl
if args.title and has_collapsible_toggle() then
            :addClass('collapsible')
if args.state == 'collapsed' then args.state = 'mw-collapsed' end
            :addClass(args.state or 'autocollapse')
tbl
    end
:addClass('mw-collapsible')
:addClass(args.state or 'autocollapse')
    tbl:css('border-spacing', 0)
end
    if border == 'subgroup' or border == 'child' or border == 'none' then
 
        tbl
tbl:css('border-spacing', 0)
            :addClass('navbox-subgroup')
if border == 'subgroup' or border == 'none' then
            :cssText(args.bodystyle)
tbl
            :cssText(args.style)
:addClass('navbox-subgroup')
    else -- regular navobx - bodystyle and style will be applied to the wrapper table
:cssText(args.bodystyle)
        tbl
:cssText(args.style)
            :addClass('navbox-inner')
else -- regular navbox - bodystyle and style will be applied to the wrapper table
            :css('background', 'transparent')
tbl
            :css('color', 'inherit')
:addClass('navbox-inner')
    end
:css('background', 'transparent')
    tbl:cssText(args.innerstyle)
:css('color', 'inherit')
end
    renderTitleRow(tbl)
tbl:cssText(args.innerstyle)
    renderAboveRow(tbl)
 
    for i, listnum in ipairs(listnums) do
renderTitleRow(tbl)
        renderListRow(tbl, listnum)  
renderAboveRow(tbl)
    end
for i, listnum in ipairs(listnums) do
    renderBelowRow(tbl)
renderListRow(tbl, listnum)
   
end
    return tbl
renderBelowRow(tbl)
 
return tbl
end
end


function p._navbox(navboxArgs)
function p._navbox(navboxArgs)
    args = navboxArgs
args = navboxArgs
   
local hiding_templatestyles = move_hiding_templatestyles(args)
    for k, v in pairs(args) do
listnums = {}
        local listnum = ('' .. k):match('^list(%d+)$')
        if listnum then table.insert(listnums, tonumber(listnum)) end
    end
    table.sort(listnums)
    border = trim(args.border or args[1] or '')


    -- render the main body of the navbox
for k, v in pairs(args) do
    local tbl = renderMainTable()
local listnum = ('' .. k):match('^list(%d+)$')
if listnum then table.insert(listnums, tonumber(listnum)) end
end
table.sort(listnums)


    -- render the appropriate wrapper around the navbox, depending on the border param
border = mw.text.trim(args.border or args[1] or '')
    local res = mw.html.create()
if border == 'child' then
    if border == 'none' then
border = 'subgroup'
        res:node(tbl)
end
    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
-- render the main body of the navbox
        -- therefore inside a div with padding:0em 0.25em. We start with a </div> to avoid the
local tbl = renderMainTable()
        -- padding being applied, and at the end add a <div> to balance out the parent's </div>
 
        res
-- render the appropriate wrapper around the navbox, depending on the border param
            :wikitext('</div>') -- mw.html 未支持 unclosed
local res = mw.html.create()
            :node(tbl)
if border == 'none' then
            :wikitext('<div>') -- mw.html 未支持 unclosed
res:node(loadTemplateStyles(hiding_templatestyles))
    else
local nav = res:tag('div')
        res
:attr('role', 'navigation')
            :tag('table')
:node(tbl)
                :attr('cellspacing', 0)
-- aria-labelledby title, otherwise above, otherwise lone group
                :addClass('navbox')
if args.title or args.above or (args.group1 and not args.group2) then
                :css('border-spacing', 0)
nav:attr('aria-labelledby', mw.uri.anchorEncode(args.title or args.above or args.group1))
                :cssText(args.bodystyle)
else
                :cssText(args.style)
nav:attr('aria-label', 'Navbox')
                :tag('tr')
end
                    :tag('td')
elseif border == 'subgroup' then
                        :css('padding', '2px')
-- We assume that this navbox is being rendered in a list cell of a parent navbox, and is
                        :node(tbl)
-- therefore inside a div with padding:0em 0.25em. We start with a </div> to avoid the
    end
-- padding being applied, and at the end add a <div> to balance out the parent's </div>
res
    renderTrackingCategories(res)
:wikitext('</div>') -- mw.html 未支持 unclosed
:node(tbl)
    return tostring(res)
:wikitext('<div>') -- 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
end
 
function p.navbox(frame)
function p.navbox(frame)
    if not getArgs then
if not getArgs then
    getArgs = require('Module:Arguments').getArgs
getArgs = require('Module:Arguments').getArgs
    end
end
    args = getArgs(frame, {wrappers = 'Template:Navbox'})
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.
-- Read the arguments in the order they'll be output in, to make references number in the right order.
    local _
local _
    _ = args.title
_ = args.title
    _ = args.above
_ = args.above
    for i = 1, 35 do
for i = 1, 35 do
        _ = args["group" .. tostring(i)]
_ = args["group" .. tostring(i)]
        _ = args["list" .. tostring(i)]
_ = args["list" .. tostring(i)]
    end  
end
    _ = args.below
_ = args.below


    return p._navbox(args)
return p._navbox(args)
end
end
 
return p
return p

2024年10月14日 (一) 03:54的版本

此模块的文档可以在Module:Navbox/doc创建

--
-- This module implements {{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 = '[[Category:孤立的子导航框]]'
	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) == '</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(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('[[Category:' .. cat .. ']]')
	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 </div> to avoid the
		-- padding being applied, and at the end add a <div> to balance out the parent's </div>
		res
			:wikitext('</div>') -- mw.html 未支持 unclosed
			:node(tbl)
			:wikitext('<div>') -- 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