跳转到内容
打开/关闭菜单
  • 486 条目
  • 873 文件
  • 22 用户
  • 9656 编辑
Blockland Wiki
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。

模块:Navbox:修订间差异

来自Blockland Wiki
无编辑摘要
标签:手工回退
无编辑摘要
标签:已被回退
第1行: 第1行:
--
--
-- This module implements {{Navbox}}
-- Module:Navbox
-- 移除了 Navbar (VDE) 功能
-- 新增独立「编」按钮
-- 支持 name / editname 指定编辑模板
-- 支持直接调用模块时自动识别模板
--
--


local p = {}
local p = {}


local navbar = require('Module:Navbar')._navbar
local getArgs -- lazily initialized
local getArgs -- lazily initialized


第11行: 第14行:
local border
local border
local listnums
local listnums
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
local ODD_EVEN_MARKER = '\127_ODDEVEN_\127'
local RESTART_MARKER = '\127_ODDEVEN0_\127'
local RESTART_MARKER = '\127_ODDEVEN0_\127'
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'
local REGEX_MARKER = '\127_ODDEVEN(%d?)_\127'


--
-- 奇偶行样式处理
--
local function striped(wikitext)
local function striped(wikitext)
-- Return wikitext with markers replaced for odd/even striping.
    local orphanCat = '[[Category:Navbox orphans]]'
-- Child (subgroup) navboxes are flagged with a category that is removed
 
-- by parent navboxes. The result is that the category shows all pages
    if border == 'subgroup' and args.orphan ~= 'yes' then
-- where a child navbox is not contained in a parent navbox.
        return wikitext .. orphanCat
local orphanCat = '[[Category:Navbox orphans]]'
    end
if border == 'subgroup' and args.orphan ~= 'yes' then
 
-- No change; striping occurs in outermost navbox.
    local first, second = 'odd', 'even'
return wikitext .. orphanCat
 
end
    if args.evenodd then
local first, second = 'odd', 'even'
        if args.evenodd == 'swap' then
if args.evenodd then
            first, second = second, first
if args.evenodd == 'swap' then
        else
first, second = second, first
            first = args.evenodd
else
            second = first
first = args.evenodd
        end
second = first
    end
end
 
end
    local changer
local changer
 
if first == second then
    if first == second then
changer = first
        changer = first
else
    else
local index = 0
        local index = 0
changer = function (code)
 
if code == '0' then
        changer = function(code)
-- Current occurrence is for a group before a nested table.
            if code == '0' then
-- Set it to first as a valid although pointless class.
                index = 0
-- The next occurrence will be the first row after a title
                return first
-- in a subgroup and will also be first.
            end
index = 0
 
return first
            index = index + 1
end
            return index % 2 == 1 and first or second
index = index + 1
        end
return index % 2 == 1 and first or second
    end
end
 
end
    local regex = orphanCat:gsub('([%[%]])', '%%%1')
local regex = orphanCat:gsub('([%[%]])', '%%%1')
 
return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer)) -- () omits gsub count
    return (wikitext:gsub(regex, ''):gsub(REGEX_MARKER, changer))
end
end


--
-- 列表内容处理
--
local function processItem(item, nowrapitems)
local function processItem(item, nowrapitems)
if item:sub(1, 2) == '{|' then
    if not item then
-- Applying nowrap to lines in a table does not make sense.
        return nil
-- Add newlines to compensate for trim of x in |parm=x in a template.
    end
return '\n' .. item ..'\n'
 
end
    if item:sub(1, 2) == '{|' then
if nowrapitems == 'yes' then
        return '\n' .. item .. '\n'
local lines = {}
    end
for line in (item .. '\n'):gmatch('([^\n]*)\n') do
 
local prefix, content = line:match('^([*:;#]+)%s*(.*)')
    if nowrapitems == 'yes' then
if prefix and not content:match('^<span class="nowrap">') then
        local lines = {}
line = prefix .. '<span class="nowrap">' .. content .. '</span>'
 
end
        for line in (item .. '\n'):gmatch('([^\n]*)\n') do
table.insert(lines, line)
            local prefix, content = line:match('^([*:;#]+)%s*(.*)')
end
 
item = table.concat(lines, '\n')
            if prefix and not content:match('^<span class="nowrap">') then
end
                line = prefix .. '<span class="nowrap">' .. content .. '</span>'
if item:match('^[*:;#]') then
            end
return '\n' .. item ..'\n'
 
end
            table.insert(lines, line)
return item
        end
 
        item = table.concat(lines, '\n')
    end
 
    if item:match('^[*:;#]') then
        return '\n' .. item .. '\n'
    end
 
    return item
end
end


-- Separate function so that we can evaluate properly whether hlist should
-- be added by the module
local function has_navbar()
return args.navbar ~= 'off' and args.navbar ~= 'plain' and not
(not args.name and mw.getCurrentFrame():getParent():getTitle():gsub('/sandbox$', '') == 'Template:Navbox')
end


local function renderNavBar(titleCell)
--
-- 编辑目标识别
--
local function getEditTarget()
 
    -- 允许关闭编辑按钮
    if args.edit == 'off' or args.navbar == 'off' then
        return nil
    end
 
    -- 优先读取显式指定的模板名称
    local name = mw.text.trim(args.editname or args.name or '')
 
    if name ~= '' then
        local title = mw.title.new(name, 10)
 
        if title
            and title.namespace == 10
            and title.interwiki == ''
        then
            return title
        end
    end
 
    -- 尝试从模块调用来源识别模板
    local frame = mw.getCurrentFrame()
    local parent = frame:getParent()
 
    if parent then
        local parentTitle = mw.title.new(parent:getTitle())
 
        if parentTitle and parentTitle.namespace == 10 then
            local parentName = parentTitle.text:gsub('/sandbox$', '')


if has_navbar() then
            -- 排除通用 Navbox 包装模板
titleCell:wikitext(navbar{
            if parentName ~= 'Navbox'
args.name,
                and parentName ~= 'Navbox subgroup'
-- we depend on this being mini = 1 when the navbox module decides
            then
-- to add hlist templatestyles. we also depend on navbar outputting
                return parentTitle
-- a copy of the hlist templatestyles.
            end
mini = 1,
        end
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;box-shadow:none; padding:0;'
    end
})
end


    return nil
end
end


--
--
--   Title row
-- 标题行
--
--
local function renderTitleRow(tbl)
local function renderTitleRow(tbl)
if not args.title then return end
    if not args.title then
        return
    end
 
    local titleRow = tbl:tag('tr')
 
    if args.titlegroup then
        titleRow
            :tag('th')
            :attr('scope', 'row')
            :addClass('navbox-group')
            :addClass(args.titlegroupclass)
            :cssText(args.basestyle)
            :cssText(args.groupstyle)
            :cssText(args.titlegroupstyle)
            :wikitext(args.titlegroup)
    end
 
    local titleCell = titleRow:tag('th'):attr('scope', 'col')
 
    if args.titlegroup then
        titleCell:addClass('navbox-title1')
    end


local titleRow = tbl:tag('tr')
    local titleColspan = 2


if args.titlegroup then
    if args.imageleft then
titleRow
        titleColspan = titleColspan + 1
:tag('th')
    end
: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.image then
        titleColspan = titleColspan + 1
    end


if args.titlegroup then
    if args.titlegroup then
titleCell
        titleColspan = titleColspan - 1
:addClass('navbox-title1')
    end
end


local titleColspan = 2
    titleCell
if args.imageleft then titleColspan = titleColspan + 1 end
        :cssText(args.basestyle)
if args.image then titleColspan = titleColspan + 1 end
        :cssText(args.titlestyle)
if args.titlegroup then titleColspan = titleColspan - 1 end
        :addClass('navbox-title')
        :attr('colspan', titleColspan)


titleCell
:cssText(args.basestyle)
:cssText(args.titlestyle)
:addClass('navbox-title')
:attr('colspan', titleColspan)


renderNavBar(titleCell)
    --
    -- 独立「编」按钮
    --
    local editTarget = getEditTarget()


titleCell
    if editTarget then
:tag('div')
        titleCell:css('position', 'relative')
-- id for aria-labelledby attribute
 
:attr('id', mw.uri.anchorEncode(args.title))
        titleCell
:addClass(args.titleclass)
            :tag('span')
:css('font-size', '114%')
            :addClass('navbox-editlink')
:css('margin', '0 4em')
            :css('position', 'absolute')
:wikitext(processItem(args.title))
            :css('left', '0.6em')
            :css('top', '50%')
            :css('transform', 'translateY(-50%)')
            :css('font-size', '80%')
            :css('font-weight', 'normal')
            :css('line-height', '1')
            :css('white-space', 'nowrap')
            :css('z-index', '1')
            :tag('a')
            :attr('href', editTarget:fullUrl('action=edit'))
            :attr('title', '编辑模板:' .. editTarget.text)
            :attr('aria-label', '编辑模板:' .. editTarget.text)
            :css('color', 'inherit')
            :css('text-decoration', 'underline')
            :wikitext('编')
    end
 
 
    --
    -- 标题文本
    --
    titleCell
        :tag('div')
        :attr('id', mw.uri.anchorEncode(args.title))
        :addClass(args.titleclass)
        :css('font-size', '114%')
        :css('margin', '0')
        :wikitext(processItem(args.title))
end
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


local function getAboveBelowColspan()
    return ret
local ret = 2
if args.imageleft then ret = ret + 1 end
if args.image then ret = ret + 1 end
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


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


--
-- 下方内容
--
local function renderBelowRow(tbl)
local function renderBelowRow(tbl)
if not args.below then return end
    if not args.below then
        return
    end


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


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


if index == 1 and args.imageleft then
    --
row
    -- 左侧图片
:tag('td')
    --
:addClass('navbox-image')
    if index == 1 and args.imageleft then
:addClass(args.imageclass)
        row
:css('width', '1px')               -- Minimize width
            :tag('td')
:css('padding', '0px 2px 0px 0px')
            :addClass('navbox-image')
:cssText(args.imageleftstyle)
            :addClass(args.imageclass)
:attr('rowspan', #listnums)
            :css('width', '1px')
:tag('div')
            :css('padding', '0 2px 0 0')
:wikitext(processItem(args.imageleft))
            :cssText(args.imageleftstyle)
end
            :attr('rowspan', #listnums)
            :tag('div')
            :wikitext(processItem(args.imageleft))
    end


if args['group' .. listnum] then
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))
    if args['group' .. listnum] then
end
        row
            :tag('th')
            :attr('scope', 'row')
            :addClass('navbox-group')
            :addClass(args.groupclass)
            :cssText(args.basestyle)
            :css('width', args.groupwidth or '1%')
            :cssText(args.groupstyle)
            :cssText(args['group' .. listnum .. 'style'])
            :wikitext(args['group' .. listnum])
    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])
    local listCell = row:tag('td')
end


local listCell = row:tag('td')
    if args['group' .. listnum] then
        listCell:addClass('navbox-list1')
    else
        listCell:attr('colspan', 2)
    end


if args['group' .. listnum] then
    if not args.groupwidth then
listCell
        listCell:css('width', '100%')
:addClass('navbox-list1')
    end
else
listCell:attr('colspan', 2)
end


if not args.groupwidth then
    local rowstyle =
listCell:css('width', '100%')
        (index % 2 == 1) and args.oddstyle or args.evenstyle
end


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


local listText = args['list' .. listnum]
    local oddEven = ODD_EVEN_MARKER
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', (index == 1 and args.list1padding) or args.listpadding or '0em 0.25em')
:wikitext(processItem(listText, args.nowrapitems))


if index == 1 and args.image then
    -- 子导航框的奇偶样式处理
row
    if listText:sub(1, 12) == '</div><table'
:tag('td')
        or listText:find('navbox%-subgroup')
:addClass('navbox-image')
    then
:addClass(args.imageclass)
        oddEven =
:css('width', '1px')               -- Minimize width
            listText:find('<th[^>]*"navbox%-title"')
:css('padding', '0px 0px 0px 2px')
            and RESTART_MARKER
:cssText(args.imagestyle)
            or 'odd'
:attr('rowspan', #listnums)
    end
:tag('div')
 
:wikitext(processItem(args.image))
    listCell
end
        :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',
            (index == 1 and args.list1padding)
                or args.listpadding
                or '0em 0.25em'
        )
        :wikitext(processItem(listText, args.nowrapitems))
 
 
    --
    -- 右侧图片
    --
    if index == 1 and args.image then
        row
            :tag('td')
            :addClass('navbox-image')
            :addClass(args.imageclass)
            :css('width', '1px')
            :css('padding', '0 0 0 2px')
            :cssText(args.imagestyle)
            :attr('rowspan', #listnums)
            :tag('div')
            :wikitext(processItem(args.image))
    end
end
end




--
--
--   Tracking categories
-- 列表样式加载
--
--
local function addListStyles()
    local frame = mw.getCurrentFrame()
    local function _add(htmlclass, ts)
        local found = false
        for _, arg in ipairs({
            'bodyclass',
            'listclass',
            'titleclass',
            'groupclass'
        }) do
            if (args[arg] or ''):find(htmlclass) then
                found = true
                break
            end
        end


local function needsHorizontalLists()
        return found
if border == 'subgroup' or args.tracking == 'no' then
            and frame:extensionTag{
return false
                name = 'templatestyles',
end
                args = {src = ts}
local listClasses = {
            }
['plainlist'] = true, ['hlist'] = true, ['hlist hnum'] = true,
            or ''
['hlist hwrap'] = true, ['hlist vcard'] = true, ['vcard hlist'] = true,
    end
['hlist vevent'] = true,
 
}
    return
return not (listClasses[args.listclass] or listClasses[args.bodyclass])
        _add('hlist', 'Flatlist/styles.css')
        .. _add('plainlist', 'Plainlist/styles.css')
end
end


-- there are a lot of list classes in the wild, so we have a function to find
-- them and add their TemplateStyles
local function addListStyles()
local frame = mw.getCurrentFrame()
-- TODO?: Should maybe take a table of classes for e.g. hnum, hwrap as above
-- I'm going to do the stupid thing first though
-- Also not sure hnum and hwrap are going to live in the same TemplateStyles
-- as hlist
local function _addListStyles(htmlclass, templatestyles)
local class_args = { -- rough order of probability of use
'bodyclass', 'listclass', 'aboveclass', 'belowclass', 'titleclass',
'navboxclass', 'groupclass', 'titlegroupclass', 'imageclass'
}
local patterns = {
'^' .. htmlclass .. '$',
'%s' .. htmlclass .. '$',
'^' .. htmlclass .. '%s',
'%s' .. htmlclass .. '%s'
}
local found = false
for _, arg in ipairs(class_args) do
for _, pattern in ipairs(patterns) do
if mw.ustring.find(args[arg] or '', pattern) then
found = true
break
end
end
if found then break end
end
if found then
return frame:extensionTag{
name = 'templatestyles', args = { src = templatestyles }
}
else
return ''
end
end
local hlist_styles = ''
-- navbar always has mini = 1, so here (on this wiki) we can assume that
-- we don't need to output hlist styles in navbox again.
if not has_navbar() then
hlist_styles = _addListStyles('hlist', 'Flatlist/styles.css')
end
local plainlist_styles = _addListStyles('plainlist', 'Plainlist/styles.css')
return hlist_styles .. plainlist_styles
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
function p._navbox(navboxArgs)
end
    args = navboxArgs
end
 
end
    listnums = {}
 
    for k, _ in pairs(args) do
        local listnum =
            type(k) == 'string'
            and 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
 


local function hasBorders()
    --
for _, key in ipairs({'groupstyle', 'basestyle', 'abovestyle', 'belowstyle'}) do
    -- 创建表格
if tostring(args[key]):find('border', 1, true) then
    --
return true
    local tbl = mw.html.create('table')
end
        :addClass('nowraplinks')
end
        :addClass(args.bodyclass)
end


local function isIllegible()
-- require('Module:Color contrast') absent on mediawiki.org
return false
end


local function getTrackingCategories()
    --
local cats = {}
    -- 折叠功能
if needsHorizontalLists() then table.insert(cats, 'Navigational boxes without horizontal lists') end
    --
if hasBackgroundColors() then table.insert(cats, 'Navboxes using background colours') end
    if args.title
if isIllegible() then table.insert(cats, 'Potentially illegible navboxes') end
        and args.state ~= 'plain'
if hasBorders() then table.insert(cats, 'Navboxes using borders') end
        and args.state ~= 'off'
return cats
    then
end
        tbl
            :addClass('mw-collapsible')
            :addClass(
                args.state == 'collapsed'
                and 'mw-collapsed'
                or (args.state or 'autocollapse')
            )
    end


local function renderTrackingCategories(builder)
    tbl:css('border-spacing', 0)
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


--
    --
--   Main navbox tables
    -- 边框样式
--
    --
local function renderMainTable()
    if border == 'subgroup' or border == 'none' then
local tbl = mw.html.create('table')
        tbl
:addClass('nowraplinks')
            :addClass('navbox-subgroup')
:addClass(args.bodyclass)
            :cssText(args.bodystyle)
            :cssText(args.style)
    else
        tbl
            :addClass('navbox-inner')
            :css('background', 'transparent')
            :css('color', 'inherit')
    end


if args.title and (args.state ~= 'plain' and args.state ~= 'off') 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')
    renderTitleRow(tbl)
:cssText(args.bodystyle)
    renderAboveRow(tbl)
: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)
    for i, listnum in ipairs(listnums) do
renderAboveRow(tbl)
        renderListRow(tbl, i, listnum)
for i, listnum in ipairs(listnums) do
    end
renderListRow(tbl, i, listnum)
end
renderBelowRow(tbl)


return tbl
    renderBelowRow(tbl)
end


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


for k, _ in pairs(args) do
    --
if type(k) == 'string' then
    -- 加载 TemplateStyles
local listnum = k:match('^list(%d+)$')
    --
if listnum then table.insert(listnums, tonumber(listnum)) end
    local res = mw.html.create()
end
    local frame = mw.getCurrentFrame()
end
table.sort(listnums)


border = mw.text.trim(args.border or args[1] or '')
    res
if border == 'child' then
        :tag('div')
border = 'subgroup'
        :addClass('navbox-styles')
end
        :addClass('nomobile')
        :wikitext(
            frame:extensionTag{
                name = 'templatestyles',
                args = {
                    src = 'Module:Navbox/styles.css'
                }
            }
        )
        :done()


-- render the main body of the navbox
local tbl = renderMainTable()
-- get templatestyles
local frame = mw.getCurrentFrame()
local base_templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = 'Module:Navbox/styles.css' }
}
local templatestyles = ''
if args.templatestyles and args.templatestyles ~= '' then
templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args.templatestyles }
}
end
local res = mw.html.create()
-- 'navbox-styles' exists for two reasons:
--  1. To wrap the styles to work around phab: T200206 more elegantly. Instead
--   of combinatorial rules, this ends up being linear number of CSS rules.
--  2. To allow MobileFrontend to rip the styles out with 'nomobile' such that
--    they are not dumped into the mobile view.
res:tag('div')
:addClass('navbox-styles')
:addClass('nomobile')
:wikitext(base_templatestyles .. templatestyles)
:done()
-- render the appropriate wrapper around the navbox, depending on the border param
if border == 'none' then
local nav = res:tag('div')
:attr('role', 'navigation')
:wikitext(addListStyles())
: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>')
:wikitext(addListStyles())
:node(tbl)
:wikitext('<div>')
else
local nav = res:tag('div')
:attr('role', 'navigation')
:addClass('navbox')
:addClass(args.navboxclass)
:cssText(args.bodystyle)
:cssText(args.style)
:css('padding', '3px')
:wikitext(addListStyles())
: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
    --
    if border == 'subgroup' then
        res
            :wikitext('</div>')
            :wikitext(addListStyles())
            :node(tbl)
            :wikitext('<div>')
    else
        res
            :tag('div')
            :attr('role', 'navigation')
            :addClass(
                border ~= 'none'
                and 'navbox'
                or nil
            )
            :addClass(args.navboxclass)
            :cssText(args.bodystyle or args.style)
            :wikitext(addListStyles())
            :node(tbl)
    end


return striped(tostring(res))
    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', '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.
    args = getArgs(frame, {
local _
        wrappers = {
_ = args.title
            'Template:Navbox',
_ = args.above
            'Template:Navbox subgroup'
for i = 1, 20 do
        }
_ = args["group" .. tostring(i)]
    })
_ = args["list" .. tostring(i)]
end
_ = args.below


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


return p
return p

2026年10月30日 (五) 15:54的版本

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

--
-- Module:Navbox
-- 移除了 Navbar (VDE) 功能
-- 新增独立「编」按钮
-- 支持 name / editname 指定编辑模板
-- 支持直接调用模块时自动识别模板
--

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 function striped(wikitext)
    local orphanCat = '[[Category:Navbox orphans]]'

    if border == 'subgroup' and args.orphan ~= 'yes' then
        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
                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))
end


--
-- 列表内容处理
--
local function processItem(item, nowrapitems)
    if not item then
        return nil
    end

    if item:sub(1, 2) == '{|' then
        return '\n' .. item .. '\n'
    end

    if nowrapitems == 'yes' then
        local lines = {}

        for line in (item .. '\n'):gmatch('([^\n]*)\n') do
            local prefix, content = line:match('^([*:;#]+)%s*(.*)')

            if prefix and not content:match('^<span class="nowrap">') then
                line = prefix .. '<span class="nowrap">' .. content .. '</span>'
            end

            table.insert(lines, line)
        end

        item = table.concat(lines, '\n')
    end

    if item:match('^[*:;#]') then
        return '\n' .. item .. '\n'
    end

    return item
end


--
-- 编辑目标识别
--
local function getEditTarget()

    -- 允许关闭编辑按钮
    if args.edit == 'off' or args.navbar == 'off' then
        return nil
    end

    -- 优先读取显式指定的模板名称
    local name = mw.text.trim(args.editname or args.name or '')

    if name ~= '' then
        local title = mw.title.new(name, 10)

        if title
            and title.namespace == 10
            and title.interwiki == ''
        then
            return title
        end
    end

    -- 尝试从模块调用来源识别模板
    local frame = mw.getCurrentFrame()
    local parent = frame:getParent()

    if parent then
        local parentTitle = mw.title.new(parent:getTitle())

        if parentTitle and parentTitle.namespace == 10 then
            local parentName = parentTitle.text:gsub('/sandbox$', '')

            -- 排除通用 Navbox 包装模板
            if parentName ~= 'Navbox'
                and parentName ~= 'Navbox subgroup'
            then
                return parentTitle
            end
        end
    end

    return nil
end


--
-- 标题行
--
local function renderTitleRow(tbl)
    if not args.title then
        return
    end

    local titleRow = tbl:tag('tr')

    if args.titlegroup then
        titleRow
            :tag('th')
            :attr('scope', 'row')
            :addClass('navbox-group')
            :addClass(args.titlegroupclass)
            :cssText(args.basestyle)
            :cssText(args.groupstyle)
            :cssText(args.titlegroupstyle)
            :wikitext(args.titlegroup)
    end

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

    if args.titlegroup then
        titleCell:addClass('navbox-title1')
    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)


    --
    -- 独立「编」按钮
    --
    local editTarget = getEditTarget()

    if editTarget then
        titleCell:css('position', 'relative')

        titleCell
            :tag('span')
            :addClass('navbox-editlink')
            :css('position', 'absolute')
            :css('left', '0.6em')
            :css('top', '50%')
            :css('transform', 'translateY(-50%)')
            :css('font-size', '80%')
            :css('font-weight', 'normal')
            :css('line-height', '1')
            :css('white-space', 'nowrap')
            :css('z-index', '1')
            :tag('a')
            :attr('href', editTarget:fullUrl('action=edit'))
            :attr('title', '编辑模板:' .. editTarget.text)
            :attr('aria-label', '编辑模板:' .. editTarget.text)
            :css('color', 'inherit')
            :css('text-decoration', 'underline')
            :wikitext('编')
    end


    --
    -- 标题文本
    --
    titleCell
        :tag('div')
        :attr('id', mw.uri.anchorEncode(args.title))
        :addClass(args.titleclass)
        :css('font-size', '114%')
        :css('margin', '0')
        :wikitext(processItem(args.title))
end


--
-- 上下方内容的列跨度
--
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

    tbl
        :tag('tr')
        :tag('td')
        :addClass('navbox-abovebelow')
        :addClass(args.aboveclass)
        :cssText(args.basestyle)
        :cssText(args.abovestyle)
        :attr('colspan', getAboveBelowColspan())
        :tag('div')
        :attr(
            'id',
            args.title and nil or mw.uri.anchorEncode(args.above)
        )
        :wikitext(processItem(args.above, args.nowrapitems))
end


--
-- 下方内容
--
local function renderBelowRow(tbl)
    if not args.below then
        return
    end

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


--
-- 列表行
--
local function renderListRow(tbl, index, listnum)
    local row = tbl:tag('tr')

    --
    -- 左侧图片
    --
    if index == 1 and args.imageleft then
        row
            :tag('td')
            :addClass('navbox-image')
            :addClass(args.imageclass)
            :css('width', '1px')
            :css('padding', '0 2px 0 0')
            :cssText(args.imageleftstyle)
            :attr('rowspan', #listnums)
            :tag('div')
            :wikitext(processItem(args.imageleft))
    end


    --
    -- 分组标题
    --
    if args['group' .. listnum] then
        row
            :tag('th')
            :attr('scope', 'row')
            :addClass('navbox-group')
            :addClass(args.groupclass)
            :cssText(args.basestyle)
            :css('width', args.groupwidth or '1%')
            :cssText(args.groupstyle)
            :cssText(args['group' .. listnum .. 'style'])
            :wikitext(args['group' .. listnum])
    end


    --
    -- 列表主体
    --
    local listCell = row:tag('td')

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

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

    local rowstyle =
        (index % 2 == 1) and args.oddstyle or args.evenstyle

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

    local oddEven = ODD_EVEN_MARKER

    -- 子导航框的奇偶样式处理
    if listText:sub(1, 12) == '</div><table'
        or listText:find('navbox%-subgroup')
    then
        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',
            (index == 1 and args.list1padding)
                or args.listpadding
                or '0em 0.25em'
        )
        :wikitext(processItem(listText, args.nowrapitems))


    --
    -- 右侧图片
    --
    if index == 1 and args.image then
        row
            :tag('td')
            :addClass('navbox-image')
            :addClass(args.imageclass)
            :css('width', '1px')
            :css('padding', '0 0 0 2px')
            :cssText(args.imagestyle)
            :attr('rowspan', #listnums)
            :tag('div')
            :wikitext(processItem(args.image))
    end
end


--
-- 列表样式加载
--
local function addListStyles()
    local frame = mw.getCurrentFrame()

    local function _add(htmlclass, ts)
        local found = false

        for _, arg in ipairs({
            'bodyclass',
            'listclass',
            'titleclass',
            'groupclass'
        }) do
            if (args[arg] or ''):find(htmlclass) then
                found = true
                break
            end
        end

        return found
            and frame:extensionTag{
                name = 'templatestyles',
                args = {src = ts}
            }
            or ''
    end

    return
        _add('hlist', 'Flatlist/styles.css')
        .. _add('plainlist', 'Plainlist/styles.css')
end


--
-- 主导航框生成
--
function p._navbox(navboxArgs)
    args = navboxArgs

    listnums = {}

    for k, _ in pairs(args) do
        local listnum =
            type(k) == 'string'
            and 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


    --
    -- 创建表格
    --
    local tbl = mw.html.create('table')
        :addClass('nowraplinks')
        :addClass(args.bodyclass)


    --
    -- 折叠功能
    --
    if args.title
        and args.state ~= 'plain'
        and args.state ~= 'off'
    then
        tbl
            :addClass('mw-collapsible')
            :addClass(
                args.state == 'collapsed'
                and 'mw-collapsed'
                or (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
        tbl
            :addClass('navbox-inner')
            :css('background', 'transparent')
            :css('color', 'inherit')
    end


    --
    -- 渲染各个部分
    --
    renderTitleRow(tbl)
    renderAboveRow(tbl)

    for i, listnum in ipairs(listnums) do
        renderListRow(tbl, i, listnum)
    end

    renderBelowRow(tbl)


    --
    -- 加载 TemplateStyles
    --
    local res = mw.html.create()
    local frame = mw.getCurrentFrame()

    res
        :tag('div')
        :addClass('navbox-styles')
        :addClass('nomobile')
        :wikitext(
            frame:extensionTag{
                name = 'templatestyles',
                args = {
                    src = 'Module:Navbox/styles.css'
                }
            }
        )
        :done()


    --
    -- 输出导航框
    --
    if border == 'subgroup' then
        res
            :wikitext('</div>')
            :wikitext(addListStyles())
            :node(tbl)
            :wikitext('<div>')
    else
        res
            :tag('div')
            :attr('role', 'navigation')
            :addClass(
                border ~= 'none'
                and 'navbox'
                or nil
            )
            :addClass(args.navboxclass)
            :cssText(args.bodystyle or args.style)
            :wikitext(addListStyles())
            :node(tbl)
    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'
        }
    })

    return p._navbox(args)
end

return p