文档图示 模块文档[查看] [编辑] [历史] [清除缓存]

这是{{NavboxV2}}的Lua实现代码。

简介

合并了{{Navbox}}相关的一系列模板。融合了{{Navbox}}的行式、{{Navbox subgroup}}的子代模块包含、{{Navbox with columns}}的列式,{{Navbox with collapsible groups}})的折叠行式。

改写自模块:Navbox(oldid=42280913)。

设计用途

Category:引用模板后大小超过限制的页面中,有相当一部分页面是由于{{Navbox}}模板超载导致。

  • 根据WP:模板限制中“嵌套展开”的说法,相同页面的多次嵌套调用是会被分次统计的(例如:页面A嵌入页面B,页面B嵌入页面C,页面C相对页面A统计到的展开字节数是被计算了2次)。而现在Navbox的子代块、列式,折叠行式的实现都是基于Navbox行式的模板调用或类似样式结构迭代,这样就符合内部多次调用Navbox的条件,页面很容易会超过模版展开后大小的限制。
  • 其次,实际上Lua的运行限制条件相当宽裕,50MB的内存限制,10秒的运行时限制,很多页面实际使用只在十分之一左右或以下,可以被大量压榨性能。

所以将Navbox所有的实现全部以Lua实现,希望能腾出解释器运行量到Lua运行量,降低解析器触发展开后大小限制的可能。

效果

在对于包含一层子Navbox的情况,展开后大小下降最多有50~60%左右。

对比例子

项目 {{Navbox}}系列,参考基准 本模板
Template:哈利波特 Special:固定链接/60036259 User:Cwek/工作室2/Navbox/T:哈利波特
Template:广州 Special:固定链接/60035252 User:Cwek/工作室2/Navbox/T:广州
Template:广州地铁车站 Special:固定链接/59586101 User:Cwek/工作室2/Navbox/T:广州地铁
Template:粤港澳大湾区公共交通 Special:固定链接/59654656 User:Cwek/工作室2/Navbox/T:粤港澳大湾区公共交通
Template:JR东日本的车辆 Special:固定链接/61531803 Special:固定链接/62385808
Template:中华人民共和国广播电视付费频道 Special:固定链接/77500465 Special:固定链接/77504799

参数

与{{Navbox}}系列模板几乎兼容。但新增部分参数填入:

  • type:Navbox的类型,对应值为vertical(对应{{Navbox}})、horizontal(对应{{Navbox with columns}})、vertical_collapsible(对应{{Navbox with collapsible groups}}),默认值为vertical
  • border:Navbox的隐藏参数,用于控制Navbox的边框机制来使子Navbox能被嵌入到父Navbox的值字段(例如listcol等)中,实际对应{{Navbox subgroup}}的实现机制。对应值为childsubgroup任一个。
在本模板添加子Navbox层时,必须传入这两个参数,这是本模板区分是否存在子Navbox层的依赖。本模板首层Navbox层无需添加border,按需添加type
  • removeGroupPadding:用于区别{{Navbox|child}}和{{Navbox subgroup}},后者在Groupn字段的单元格增加一组padding的配置,适用于子Navbox层。任意值,存在则可,为移除该padding配置(对应{{Navbox|child}})。

自{{Navbox}}系列模板转换

将原有嵌入{{Navbox}}系列模板的值字段listn(其他类同)改为listn-,并作为相应嵌套子Navbox模板的参数的前缀来加入,使这些模板嵌套转换为扁平化的一层模板参数。

例子
{{Navbox}}系列 本模板
{{Navbox
|name = Navbox/doc
|state = uncollapsed
|image = {{{image}}}
|imageleft = {{{imageleft}}}
|title = {{{title}}}
|above = {{{above}}}
|group1 = {{{group1}}}
|list1 = {{Navbox subgroup
 | title = {{{list1-title}}}
 | above = {{{list1-above}}}
 | below = {{{list1-below}}}
 | imageleft = {{{list1-imageleft}}}
 | image = {{{list1-image}}}
 | group1 = {{{list1-group1}}}
 | list1  = {{{list1-list1}}}
 | group2 = {{{list1-group2}}}
 | list2  = {{{list1-list2}}}
}}
|group2 = {{{group2}}}
|list2 = {{Navbox subgroup
| group1 = {{{list2-group1}}}
| list1  = {{{list2-list1}}}
| group2 = {{{list2-group2}}}
| list2  = {{{list2-list2}}}
}}
|below = {{{below}}}
}}
{{NavboxV2
|name = Navbox/doc
|state = uncollapsed
|image = {{{image}}}
|imageleft = {{{imageleft}}}
|title = {{{title}}}
|above = {{{above}}}
<!--  list1 -->
|group1 = {{{group1}}}
<!-- list1-sub-->
|list1-type =vertical <!--作为list1的子Navbox层,全部相应参数加上对应前缀“list1-”,下同,如此类推 -->
|list1-border=child
|list1-title = {{{list1-title}}}
|list1-above = {{{list1-above}}}
|list1-below = {{{list1-below}}}
|list1-imageleft = {{{list1-imageleft}}}
|list1-image = {{{list1-image}}}
|list1-group1 = {{{list1-group1}}}
|list1-list1  = {{{list1-list1}}}
|list1-group2 = {{{list1-group2}}}
|list1-list2  = {{{list1-list2}}}
<!--  list2 -->
|group2 = {{{group2}}}
<!-- list2-sub-->
|list2-type =vertical <!--作为list2的子Navbox层,全部相应参数加上对应前缀“list2-”,下同,如此类推 -->
|list2-border=child
|list2-group1 = {{{list2-group1}}}
|list2-list1  = {{{list2-list1}}}
|list2-group2 = {{{list2-group2}}}
|list2-list2  = {{{list2-list2}}}
<!--end-->
|below = {{{below}}}
}}

转换注意

由于{{Navbox}}系列的实现较为复杂和涉及自我嵌套,本模板的实现也为此做了对应兼容性调整,可能会出现一些参数被过度透传(可能在样式控制部分,原因是原有设计通过控制参数传入来隔离,而本设计为了使参数扁平化,导致部分这些参数无法隔离)。而且模板参数非常依赖命名规律,在转换替换前,请进行testcase检查,确认转换后能与原来的样式、功能基本一致,才应用转换。如果出现问题,请保留案例并联系本模板维护编辑协助处理,或者放弃

虽然可以在值字段(例如listcol等)重新嵌入{{Navbox}}系列模板,但这和原有做法一样,失去了本模板降低解析器限制的作用,不建议这样做。

--
-- This module will implement {{Navbox}}
--
local p = {}

-- Context start
local _NavboxContext = {}
local __END__ = {['__end__']='__end__'}
local NavboxContextNewObj = function(_prefix, _level, _type, _parent)
    return {
        ['prefix'] = _prefix,
        ['level'] = _level,
        ['type'] = _type,
        ['parent'] = _parent,
        ['_Context'] = {}
    }
end

local NavboxContextMetaFunction = {
    __index = function(_obj, key)
        local _key = tostring(key)
        if _key == 'prefix' or _key == 'level' or _key == 'type' or _key == 'parent' then
            return _obj[_key]
        else
            return _obj._Context[_key]
        end
    end,
    __newindex = function(_obj, key, val)
        local _key = tostring(key)
        if _key == 'prefix' or _key == 'level' or _key == 'type' or _key == 'parent' then
            _obj[_key] = val
        else
            _obj._Context[_key] = val
        end
    end,
    __tostring = function(_obj)
        local output = {}
        table.insert(output, 'prefix=' .. _obj['prefix'])
        table.insert(output, 'level=' .. _obj['level'])
        table.insert(output, 'type=' .. _obj['type'])
        local t_parent=_obj['parent']
        if t_parent==__END__ then
         table.insert(output, 'parent=' .. tostring(nil))
     else
      table.insert(output, 'parent=<'..t_parent.level..','..t_parent.prefix..'>')
     end
        for k, v in pairs(_obj._Context) do
         local t_v=v
         if t_v == nil then
          t_v=''
         elseif type(t_v)=='table' then
          t_v=tableToString(t_v)
      end
            table.insert(output, k .. '=' .. t_v)
        end
        return 'context:{\n' .. table.concat(output, ",\n") .. '\n}'
    end
}

_NavboxContext['END']=__END__
_NavboxContext.new = function(prefix, level, t_type, parent)
    local _prefix = prefix or ""
    local _level = level or 1
    local _type = t_type or ''
    local _parent = parent or __END__

    local newobj = NavboxContextNewObj(_prefix, _level, _type, _parent)
    setmetatable(newobj, NavboxContextMetaFunction)
    return newobj
end
-- Context end

local navbarFunc = require('Module:Navbar')._navbar
local NavboxContext = _NavboxContext
local getArgs -- lazily initialized
local DEBUG = false
p.NavboxContext = NavboxContext

-- 全局性参数锚
local args

-- 常量定义 (Constant Define)
local Limit = {
    vertical = 35,
    horizontal = {col = 20, list = 6},
    vertical_collapsible = 20,
    child = 10
}

local NavType = {
    V = "vertical" -- 垂直,list
    ,
    H = "horizontal" -- 水平,col
    ,
    VC = "vertical_collapsible" -- 折叠垂直
}

local MainTemplateName = 'Template:NavboxV2'

---------------------------------------------------------------
--
-- 工具箱方法 (Util Function)
--

local trim = function(s)
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end

local addNewline = function(s)
    if s:match('^[*:;#]') or s:match('^{|') then
        return '\n' .. s .. '\n'
    else
        return s
    end
end

---数组除重
local removeDump = function(arr)
    local _t1, _t2 = {}, {}
    for _, val in pairs(arr) do _t1[val] = true end
    for key, _ in pairs(_t1) do table.insert(_t2, key) end
    return _t2
end

function tableToString(_table)
    local outputs = {}
    if _table == nil then
        table.insert(outputs, '<nil>')
    elseif type(_table) == 'table' then
        for k, v in pairs(_table) do
            local output
            if type(v) == 'table' then
                output = tableToString(v)
            else
                output = tostring(v)
            end
            table.insert(outputs, tostring(k) .. "=" .. output)
        end
    end
    return '{' .. table.concat(outputs, ",") .. '}'
end

function debugLog(...)
    if DEBUG then
        if #arg == 1 then
            mw.log(tostring(arg[1]))
            mw.log("--------------------")
        else
            for k, v in pairs(arg) do
                local pass = false or tostring(k) == 'n'
                if not pass then
                    local _v = v
                    if type(v) == 'table' then
                        _v = tableToString(v)
                    end
                    mw.log(tostring(_v))
                end
            end
        end
    end
end

---------------------------------------------------------------
--
-- 功能性方法 (Functional Function)
--

---获得有效的类型
local getValidType = function(input, defVal)
    if NavType.V == input or NavType.H == input or NavType.VC == input then
        return input
    end
    return defVal
end

---检查border判断是不是子Navbox
local borderIsChild = function(border)
    if border == 'subgroup' or border == 'child' then
        return true
    end
    return false
end

---获得参数
local getArg = function(_a, _b, defVal, context, _contextKey)
    local contextKey = _contextKey or _b
    if context and contextKey and context[contextKey] then
        -- debugLog('getArg By Context',contextKey)
        return context[contextKey]
    else
        local prefix = _a ~= nil and _a or ""
        local key = (_b == nil and _a ~= nil) and _a or _b
        local argsKey = prefix .. (prefix == "" and "" or "-") .. key
        -- debugLog('getArg By InputArg',argsKey)
        return args[argsKey] or defVal
    end
end

---子Navbox参数组判断
local checkHaveChild = function(prefix, valuekey)
    if getValidType(getArg(prefix, valuekey .. '-' .. 'type'), nil) and
       borderIsChild(getArg(prefix, valuekey .. '-' .. 'border')) then
        return true
    end
    return false
end

---获得listnum
local getListnum = function(prefix, limit, contentEqList)
    debugLog("getListnum", {['prefix'] = prefix, ['limit'] = limit})
    prefix = prefix:gsub('(-)', '%%-')
    local listnums = {}
    for k, v in pairs(args) do
        k = ('' .. k)
        -- debugLog("getListnum,k=",k)
        local listnum =
        (
            k:match('^' .. prefix .. (prefix == "" and "" or "%-") .. 'list(%d+)$') or
            k:match('^' .. prefix .. (prefix == "" and "" or "%-") .. 'list(%d+)%-')
        ) or
        (
            contentEqList and( -- VerticalCollapsibleTable 的 Content适配
            k:match('^' .. prefix .. (prefix == "" and "" or "%-") .. 'content(%d+)$') or
            k:match('^' .. prefix ..(prefix == "" and "" or "%-") ..  'content(%d+)%-')
            ) or nil
        )
        if listnum and tonumber(listnum) <= limit then
            listnum = tonumber(listnum)
            table.insert(listnums, listnum)
            -- debugLog("getListnum,listnum=",listnum)
        end
    end
    listnums = removeDump(listnums)
    table.sort(listnums)
    debugLog('list:', listnums)
    debugLog('getListnum End')
    return listnums
end

---参数检查和摇匀
function p.shakeArgs(prefix, level)
    local _
    local list_limit = 0

    _ = getArg(prefix, "title")
    _ = getArg(prefix, "above")

    if getArg(prefix, "type") == NavType.H then
        list_limit = Limit.horizontal.list
        for i = 1, Limit.horizontal.col do
            _ = getArg(prefix, "col" .. tostring(i) .. "header")
            if checkHaveChild(prefix, "col" .. tostring(i) .. "header") then
                p.shakeArgs( (prefix == "" and "" or "-") .. "col" .. tostring(i) .. "header", level + 1)
            end

            _ = getArg(prefix, "col" .. tostring(i))
            if checkHaveChild(prefix, "col" .. tostring(i)) then
                p.shakeArgs((prefix == "" and "" or "-") .. "col" .. tostring(i), level + 1)
            end

            _ = getArg(prefix, "col" .. tostring(i) .. "footer")
            if checkHaveChild(prefix, "col" .. tostring(i) .. "footer") then
                p.shakeArgs( (prefix == "" and "" or "-") .. "col" .. tostring(i) .."footer", level + 1)
            end
        end
    end

    if getArg(prefix, "type") == NavType.V then
        list_limit = Limit.vertical
    elseif getArg(prefix, "type") == NavType.VC then
        list_limit = Limit.vertical_collapsible
    end
    for i = 1, list_limit do
        _ = getArg(prefix, "group" .. tostring(i))
        _ = getArg(prefix, "list" .. tostring(i))

        if checkHaveChild(prefix, "list" .. tostring(i)) then
            -- debugLog('shakeArgs',prefix,"list"..tostring(i))
            p.shakeArgs((prefix == "" and "" or "-") .. "list" .. tostring(i), level + 1)
        end
        if checkHaveChild(prefix, "content" .. tostring(i)) then
            p.shakeArgs( (prefix == "" and "" or "-") .. "content" .. tostring(i),level + 1)
        end
    end

    _ = getArg(prefix, "below")
end
---------------------------------------------------------------
--
--  元素渲染方法 (Element Render)
--

---创建表头
local function createNavTableHeader(context)
    debugLog('render TableHeader', context)
    local prefix = context.prefix
    local state, title, border = getArg(prefix, "state", nil, context),
                                 getArg(prefix, "title"),
                                 getArg(prefix, "border", nil, context)
    debugLog('render TableHeader args', {
        ['prefix'] = prefix,
        ['state'] = state,
        ['title'] = title,
        ['border'] = border
    })

    local rootTable =
        mw.html.create('table')
            :attr('cellspacing', 0)
            :addClass('nowraplinks')
            :addClass(getArg(prefix, "bodyclass", nil, context))
            :css('border-spacing', 0)

    if title and (state ~= 'plain' and state ~= 'off') then
        if state == 'collapsed' then state = 'mw-collapsed' end
        rootTable
            :addClass('mw-collapsible')
            :addClass(state or 'autocollapse')
    end

    if borderIsChild(border) or border == 'none' then
        rootTable
            :addClass('navbox-subgroup')
            :cssText(getArg(prefix,"bodystyle", nil,context))
            :cssText(getArg(prefix, "style", nil, context))
    else -- regular navobx - bodystyle and style will be applied to the wrapper table
        rootTable
            :addClass('navbox-inner')
            :css('background', 'transparent')
            :css('color', 'inherit')
    end

    rootTable:cssText(getArg(prefix, "innerstyle"))

    debugLog('render TableHeader End')
    return rootTable
end

---分割行 (SplitRow)
local function renderSplitRow(rootTable, context)
    local colspan = context.splitRowcolspan or context.totalColspan
    debugLog('render SplitRow', {
        ['needAddSplitRow'] = context.needAddSplitRow,
        ['colspan'] = colspan
    }, context)

    if context.needAddSplitRow then
        rootTable
            :tag('tr')
                :css('height', '2px')
                :tag('td')
                    :attr('colspan',colspan)
    end

    context.splitRowcolspan = colspan
    context.needAddSplitRow = true
end

---创建三键导航
local function renderNavBar(titleCell, context)
    local prefix = context.prefix
    local navbar, state, border, name, titlestyle =
        getArg(prefix, "navbar", nil, context),
        getArg(prefix, "state", nil, context),
        getArg(prefix, "border", nil, context),
        getArg(prefix, "name"),
        getArg(prefix, 'titlestyle', nil, context)
    debugLog('render Navbar', {
        ['prefix'] = prefix,
        ['navbar'] = navbar,
        ['state'] = state,
        ['border'] = border,
        ['name'] = name,
        ['titlestyle'] = titlestyle
    }, context)
    -- 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 navbar == 'off' then
        -- No navbar, and client wants no spacer, i.e. wants the title to be shifted to the left. If there's
        -- also no show/hide link, then we need a spacer on the right to achieve the left shift.
        if state == 'plain' then spacerSide = 'right' end
    elseif navbar == 'plain' or (
        (not name) and
        mw.getCurrentFrame():getParent():getTitle() == MainTemplateName and
        (borderIsChild(border) or border == 'none')
    ) then
        -- No navbar. Need a spacer on the left to balance out the width of the show/hide link.
        if state ~= 'plain' then spacerSide = 'left' end
    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.
        if state == 'plain' then spacerSide = 'right' end

        titleCell
            :wikitext(navbarFunc {
                name,
                mini = 1,
                fontstyle = table.concat({
                    'color:inherit',
                    getArg(prefix, 'basestyle', nil, context) or '',
                    (titlestyle or ''),
                    'background:none transparent;border:none'
                }, ';')
            })
    end

    -- Render the spacer div.
    if spacerSide then
        titleCell
            :tag('span')
            	:addClass("spacer")
                :css('float', spacerSide)
                :css('position', 'absolute')
                :css(spacerSide, '1em')
                :css('margin-' ..(spacerSide == 'left' and 'right' or'left'), '0.5em')
                :css('padding-' .. spacerSide, '0.2em')
                :wikitext('&nbsp;')
    end

    debugLog('render Navbar End')
end

---标题行
local function renderTitleRow(rootTable, context)
    local prefix = context.prefix
    local title = getArg(prefix, "title", nil, context)
    if not title then return end
    debugLog('render TitleRow', {
        ['prefix'] = prefix,
        ['title'] = title
    },context)
    local basestyle = getArg(prefix, "basestyle", nil, context)
    renderSplitRow(rootTable, context)
    local titleRow = rootTable:tag('tr')

    local titlegroup = getArg(prefix, "titlegroup")
    if titlegroup and (not context.notNeedTitlegroup) then
        titleRow
            :tag('th')
                :attr('scope', 'row')
                :addClass('navbox-group')
                :addClass(getArg(prefix, "titlegroupclass"))
                :cssText(basestyle)
                :cssText(getArg(prefix, "groupstyle", nil, context))
                :cssText(getArg(prefix,"titlegroupstyle"))
                :wikitext(titlegroup)
    end

    local titleCell = titleRow:tag('th'):attr('scope', 'col')
    local titleStyle = getArg(prefix, "titlestyle", nil, context)
    local titleColspan = context.totalColspan
    if titlegroup and (not context.notNeedTitlegroup) then
        titleCell
            :css('border-left', '2px solid #fdfdfd')
            :css('width', '100%')

        titleColspan = titleColspan - 1
    end
    titleCell
        :cssText(basestyle)
        :cssText(titleStyle)
        :addClass('navbox-title')
        :addClass(getArg(prefix, "titleclass", nil, context))
        :attr('colspan',titleColspan)

    renderNavBar(titleCell, context)

    titleCell
        :tag('div')
            :css('font-size', '110%')
            :wikitext(addNewline(title))

    debugLog('render TitleRow End')
end

---上列
local function renderAboveRow(rootTable, context)
    local prefix = context.prefix
    local above = getArg(prefix, "above")
    if not above then return end
    debugLog('render AboveRow', {['prefix'] = prefix}, context)
    renderSplitRow(rootTable, context)

    local Colspan = context.totalColspan
    local AboveRow = rootTable:tag('tr')
    AboveRow
        :tag('td')
            :addClass('navbox-abovebelow')
            :addClass(getArg(prefix,"aboveclass"))
            :cssText(getArg(prefix, "basestyle"))
            :cssText(getArg(prefix,"abovestyle"))
            :attr('colspan', Colspan)
            :tag('div')
                :wikitext(addNewline(above))

    debugLog('render AboveRow End')
end

---下列
local function renderBelowRow(rootTable, context)
    local prefix = context.prefix
    local below = getArg(prefix, "below")
    if not below then return end
    debugLog('render BelowRow', {['prefix'] = prefix}, context)
    renderSplitRow(rootTable, context)

    local Colspan = context.totalColspan
    local BelowRow = rootTable:tag('tr')
    BelowRow
        :tag('td')
            :addClass('navbox-abovebelow')
            :addClass(getArg(prefix,"belowclass"))
            :cssText(getArg(prefix, "basestyle"))
            :cssText(getArg(prefix,"belowstyle"))
            :attr('colspan', Colspan)
            :tag('div')
                :wikitext(addNewline(below))

    debugLog('render BelowRow End')
end

---------------------------------------------------------------

-- 数据列的方法生成器
local function _renderColRow_FunctionBuilder(rootTable, context, nodeFunc)
    debugLog("_renderColRow_FunctionBuilder builded", {['context'] = context})
    return function(listCellDivToWrite, divNotClose)
        debugLog(debug.traceback('FunctionInrenderColRow'),
                 {['context'] = context, ['divNotClose'] = divNotClose})

        if not divNotClose then
            listCellDivToWrite:done() -- div end
            :node(rootTable and
                nodeFunc(rootTable, context) or
                nodeFunc(context)
            )
            :tag('div'):done()
        else
            listCellDivToWrite:node(nodeFunc(rootTable, context))
        end
    end
end

---数据行,统一的实现
local function _renderListRow(rootTable, context, OtherListFunction)
    renderSplitRow(rootTable, context)

    local prefix, level = context.prefix, context.level
    local listnum = context.listnum or 1
    local isFirst, isOdd = (listnum == 1), (listnum % 2) == 1
    local ImageRowspan = 2 * context.totalRowspan - 1 + (context['imageCellCompensate'] or 0)
    local notNeedImage, notNeedGroup = context.notNeedImage,context.notNeedGroup
    debugLog('ValueRow Implement', {
        ['prefix'] = prefix,
        ['listnum'] = listnum,
        ['ImageRowspan'] = ImageRowspan,
        ['HaveOtherListFunction'] = tostring(not not OtherListFunction),
        ['notNeedImage'] = notNeedImage,
        ['notNeedGroup'] = notNeedGroup
    }, context)
    
    local listRow = rootTable:tag('tr')
    local groupCell, listCell
    local insertImage = false

    -- image
    local imageLeft, image, insertImage =
        getArg(prefix, "imageleft", nil, context),
        getArg(prefix, "image", nil, context),
        false
    -- CollapsibleListRow 适配
    if context.notImageLeftCell then imageLeft = nil end
    if context.notImageCell then image = nil end
    if isFirst and (not notNeedImage) then
        if imageLeft then
            debugLog('imageLeftRow', {['imageLeft'] = imageLeft})
            listRow
                :tag('td')
                    :addClass('navbox-image')
                    :addClass(getArg(prefix, "imageclass", nil, context))
                    :css('width', '0%')
                    :css('padding', '0px 2px 0px 0px')
                    :cssText(getArg(prefix,"imageleftstyle",nil, context))
                    :attr('rowspan', ImageRowspan)
                        :tag('div')
                            :wikitext(addNewline(imageLeft))
                        :done() -- div done
                    :done() -- td done
        end
        if image then insertImage = true end
    end

    -- list pre
    local listHaveChild = checkHaveChild(prefix, 'list' .. listnum)
    local contentHaveChild = context.contentEqList and checkHaveChild(prefix, 'content' .. listnum)

    -- group
    local group, groupwidth, grouppadding, removeGroupPadding =
        getArg(prefix,"group" ..listnum,nil,context),
        ((group and getArg(prefix,"groupwidth")) or nil),
        (getArg(prefix,"grouppadding") or '0em 0.75em;'),
        (getArg(prefix,'removeGroupPadding', false) and level > 1)
    local navboxSubgroupMark =
		borderIsChild(getArg(prefix, "border")) and
        (getArg(prefix, 'type') == NavType.V) and 
        (not removeGroupPadding) --针对(列式子组合)T:Navbox subgroup的适配
            
    if group and not notNeedGroup then
        debugLog('groupTh', {['group'] = group})
        groupCell = listRow
            :tag('th')
                :attr('scope', 'row')
                :addClass('navbox-group')
                :addClass(getArg(prefix, "groupclass", nil, context))
                :cssText(getArg(prefix, "basestyle"))
                :css(((groupwidth and {['width'] = groupwidth}) or {}))
                :cssText(navboxSubgroupMark and 'padding-left:0em;padding-right:0em;' or '') 
                :cssText(getArg(prefix, "groupstyle"))
                :cssText(getArg(prefix,'group' ..listnum ..'style'))

        local tempEle = groupCell
        if navboxSubgroupMark then
            groupCell:cssText("padding-left:0em;padding-right:0em;")
            tempEle = tempEle:tag("div"):css("padding", grouppadding)
        end
        tempEle:wikitext(group)
    end

    -- list
    do
        listCell = listRow:tag('td')
        if group and not notNeedGroup then
            listCell
                :css('text-align', 'left')
                :css('border-left-width', '2px')
                :css('border-left-style', 'solid')
        else
            listCell:attr('colspan', 2)
        end

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

        local evenOdd = getArg(prefix, "evenodd")
        evenOdd = (
            evenOdd == 'swap' and
            (isOdd and 'even' or 'odd') or
            (isOdd and (evenOdd or 'odd') or (evenOdd or 'even'))
        )
        local evenOddStyle = (isOdd and getArg(prefix, "evenstyle")) or getArg(prefix, "oddstyle")
        if context.lockEvenOdd then -- CollapsibleListRow 适配
            evenOdd = 'odd'
        end
        if context.noEvenOddStyle then -- CollapsibleListRow 适配
            evenOddStyle = ''
        end

        local list1padding = (notNeedGroup) and
                            getArg(prefix, "list1padding", nil, context) or
                            '0em 0.25em'
        local listNpadding = (isFirst and list1padding) or
                            (getArg(prefix, "listpadding", nil, context) or
                            '0em 0.25em')
        local listNstyle = -- ((not notNeedGroup))
                            (isFirst and getArg(prefix, 'list1style', '', context)) or
                            getArg(prefix, 'list' .. listnum .. 'style', '')
        local liststyle = getArg(prefix, "liststyle", '', context)

        listCell
            :css('padding', '0px')
            :cssText(liststyle)
            :cssText(evenOddStyle)
            :cssText(listNstyle)
            :addClass('navbox-list')
            :addClass('navbox-' ..evenOdd)
            :addClass(getArg(prefix, "listclass"))
        local tempdiv = listCell
            :tag('div')
                :css('padding', listNpadding)
        -- local listHaveChild = checkHaveChild(prefix,'list'..listnum)
        -- local contentHaveChild = context.contentEqList and checkHaveChild(prefix,'content'..listnum)
        if OtherListFunction then
            debugLog('ValueRow OtherListFunction', {
                ['otherListFunctionDivNotClose'] = context.otherListFunctionDivNotClose
            })
            -- OtherListFunction(listCell)
            OtherListFunction(tempdiv, context.otherListFunctionDivNotClose)
        elseif (listHaveChild or contentHaveChild) and level <= Limit.child then
            local listKeyName = 'list'
            if contentHaveChild then listKeyName = 'content' end

            local childContext = NavboxContext.new(
                    prefix ..(prefix == '' and '' or '-') .. listKeyName ..listnum,
                    level + 1,
                    getValidType(getArg(
                        prefix ..(prefix == '' and'' or '-') ..listKeyName ..listnum,
                        'type'
                    ),NavType.V),
                    context
            )

            debugLog('ValueRow NewChild', childContext)
            tempdiv
                :done() -- div end
                :node(p.renderNavTable(childContext))
                :tag('div'):done()
        else
            local listContent = getArg(prefix, 'list' .. listnum, '') ..
                                    (((not context.contentEqList) and '') or
                                    getArg(prefix, 'content' .. listnum, ''))
            debugLog('ValueRow listnum', {['listnum'] = listnum})
            tempdiv:wikitext(addNewline(listContent))
        end
    end
    -- end

    if insertImage then
        debugLog('imageRow', {['image'] = image})
        listRow
            :tag('td')
                :addClass('navbox-image')
                :addClass(getArg(prefix,"imageclass",nil, context))
                :css('width', '0%')
                :css('padding', '0px 0px 0px 2px')
                :cssText(getArg(prefix, "imagestyle", nil, context))
                :attr('rowspan',ImageRowspan)
                :tag('div')
                    :wikitext(addNewline(image))
    end
    debugLog('ValueRow Implement End')
end

---数据行,垂直式的具体实现
local function renderListRow(rootTable, context)
    debugLog('render ListRow', context)
    _renderListRow(rootTable, context)
    debugLog('render ListRow End')
end

---------------------------------------------------------------

---数据列,水平式的具体实现 (ColRow)
local function _renderColRow(rootTable, context)
    local prefix, level = context.prefix, context.level
    local fullwidth = getArg(prefix, "fullwidth")
    local col1header, col1, col1footer =
        getArg(prefix, 'col' .. '1' .. 'header'),
        getArg(prefix, 'col' .. '1'),
        getArg(prefix, 'col' .. '1' .. 'footer')
    debugLog('ColRow Implement', {['prefix'] = prefix}, context)
    -- new table root
    rootTable = mw.html.create('table')
    rootTable
        :addClass("navbox-columns-table")
        :attr("cellspacing", "0")
        :cssText("text-align:left;")
        :cssText((col1header or fullwidth) and "width:100%;" or
            "width:auto;margin-left:auto;margin-right:auto;")
        :cssText(getArg(prefix,"coltablestyle"))

    local headerTR, colbodyTR, footerTR = nil, nil, nil

    context.needAddSplitRow = false
    context.splitRowcolspan = 1
    -- header
    if col1header then
        renderSplitRow(rootTable, context)

        debugLog('ColRow Header', {})
        headerTR = rootTable:tag('tr')
        for colnum = 1, Limit.horizontal.col do
            debugLog('ColRow Header', colnum)
            local isFirst, isOdd = colnum == 1, (colnum % 2) == 1
            local colheaderkey = 'col' .. colnum .. 'header'
            local colNheader = isFirst and col1header or  getArg(prefix, colheaderkey)

            if headerTR and colNheader then
                debugLog('ColRow Herder Cell', {['colnum'] = colnum})
                local headerNCell = headerTR:tag('td')
                headerNCell
                    :addClass('navbox-abovebelow')
                    :cssText(isFirst and "" or "border-left:2px solid #fdfdfd;")
                    :cssText(getArg(prefix, "colheaderstyle"))
                    :cssText(getArg(prefix,colheaderkey ..'style'))
                    :attr((colnum ~= Limit.horizontal.col and
                         {['colspan'] = getArg(prefix, colheaderkey .. 'colspan', 1)}) or
                         {})
--[[            if checkHaveChild(prefix,colheaderkey) and level<= Limit.child then
                    local childContext=NavboxContext.new(colheaderkey ,level+1 ,NavType.H,context)
                    debugLog('ColRow Herder NewChild',childContext)
                    headerNCell:node(p.renderNavTable(childContext):allDone())
                else]]
                -- debugLog('ColRow Herder Cell',{['colnum']=colnum})
                headerNCell:wikitext(addNewline("'''" .. colNheader .. "'''"))
                -- end
            end
        end
        debugLog('ColRow Header End', {['colnum'] = colnum})
    end

    -- col
    local col1havechild = checkHaveChild(prefix, "col1")
    if col1 or col1havechild then
        renderSplitRow(rootTable, context)

        debugLog('ColRow Body', {['col1havechild'] = col1havechild})
        colbodyTR = rootTable:tag('tr'):cssText('vertical-align:top;')
        if not (col1header or col1footer or fullwidth) then
            local padding, test0 = getArg(prefix, "padding"), nil
            if padding then
                padding = trim(padding)
                test0 = padding:find('^0[%%%a]?[%a]?[;]?$')
            end
            if test0 ~= nil or padding == 'off' then
            else
                colbodyTR
                    :tag('td')
                        :css("width", padding or '5em')
                        :wikitext('&nbsp;&nbsp;&nbsp;')
                        :done()
            end
        end

        for colnum = 1, Limit.horizontal.col do
            debugLog('ColRow Body', colnum)
            local isFirst, isOdd = colnum == 1, (colnum % 2) == 1
            local colkey = 'col' .. colnum
            local colN = isFirst and col1 or getArg(prefix, colkey)
            local colNhavechild = isFirst and col1havechild or checkHaveChild(prefix, colkey)
            
            if colN or colNhavechild then
                local oddevenstyle = getArg(prefix, isOdd and 'oddcolstyle' or'evencolstyle')
                local colNCell = colbodyTR
                    :tag('td')
                        :css("padding", "0px")
                        :cssText(((not isFirst) and "border-left:2px solid #fdfdfd;") or '')
                        :cssText(getArg(prefix, 'colstyle'))
                        :cssText(oddevenstyle)
                        :cssText(getArg(prefix, colkey .. 'style'))
                        :css('width',
                            (getArg(prefix, colkey .. 'width') or
                            getArg(prefix, 'colwidth')) or
                            '10em')

                if checkHaveChild(prefix, colkey) and level <= Limit.child then
                    local childContext =
                        NavboxContext.new(
                            prefix .. (prefix == '' and '' or '-') .. colkey,
                            level + 1,
                            getValidType(getArg(prefix, 'type'), NavType.H),
                            context
                        )
                    debugLog('ColRow Body NewChild', childContext)
                    colNCell
                        :tag('div'):done()
                        :node(p.renderNavTable(childContext):allDone())
                        :tag('div'):done()
                else
                    debugLog('ColRow Body Cell', {['colnum'] = colnum})
                    colNCell:tag('div'):wikitext(addNewline('\n'..colN..'\n'))
                end
            end
            debugLog('ColRow Body End', {['colnum'] = colnum})
        end
    end

    -- footer
    if col1footer then
        renderSplitRow(rootTable, context)

        debugLog('ColRow footer', {})
        footerTR = rootTable:tag('tr')
        for colnum = 1, Limit.horizontal.col do
            debugLog('ColRow footer', colnum)
            local isFirst, isOdd = colnum == 1, (colnum % 2) == 1
            local colfooterkey = 'col' .. colnum .. 'footer'
            local colNfooter = isFirst and col1footer or getArg(prefix, colfooterkey)

            if colNfooter then
                debugLog('ColRow footer Cell', {['colnum'] = colnum})
                local footerNCell = footerTR:tag('td')
                footerNCell
                    :addClass('navbox-abovebelow')
                    :cssText(isFirst and "" or "border-left:2px solid #fdfdfd;")
                    :cssText(getArg(prefix, "colfooterstyle"))
                    :cssText(getArg(prefix, colfooterkey ..'style'))
                    :attr((colnum ~= Limit.horizontal.col and
                         {['colspan'] = getArg(prefix,colfooterkey .. 'colspan', 1)}) or
                         {})
--[[            if checkHaveChild(prefix,colfooterkey) and level<= Limit.child then
                    local childContext=NavboxContext.new(colfooterkey ,level+1 ,NavType.H,context)
                    debugLog('ColRow footer NewChild',childContext)
                    footerNCell:node(p.renderNavTable(childContext):allDone())
                else]]
                -- debugLog('ColRow footer Cell',{['colnum']=colnum})
                footerNCell:wikitext(addNewline("'''" .. colNfooter .. "'''"))
                -- end
            end
        end
        debugLog('ColRow footer End', {['colnum'] = colnum})
    end

    debugLog('ColRow Implement End')
    return rootTable:allDone()
end

-- 数据列,具体实现
local function renderColRow(rootTable, context)
    debugLog('renderColRow', {['context'] = context})

    context.notNeedGroup = true
    context['list1padding'] = '0px'
    context['list1style'] = "background:transparent;color:inherit;"
    context['otherListFunctionDivNotClose'] = true
    context['imageCellCompensate'] = 2

    _renderListRow(
        rootTable,
        context,
        _renderColRow_FunctionBuilder(rootTable,context,_renderColRow)
    )

    -- clean up
    context.notNeedGroup = nil
    context['list1padding'] = nil
    context['list1style'] = nil
    context['otherListFunctionDivNotClose'] = nil
    context['imageCellCompensate'] = nil
    debugLog('renderColRow End')
end

---------------------------------------------------------------

-- 折叠行式的子Nabox
local function _renderSmallNavboxInCollapsibleListRow(rootTable, context)
    local prefix, level = context.prefix, context.level
    debugLog('_renderSmallNavboxInCollapsibleListRow',
             {['prefix'] = prefix},
             {['context'] = context})
    local listnum = context.listnum

    -- 部分需要压制传入的样式
    context.bodyclass = ''
    context.titleclass = ''
    context.groupclass = ''
    context.imageclass = ''
    context.bodystyle = ''
    context.style = ''
    context.basestyle = ''
    context.imagestyle = ''
    context.imageleftstyle = ''

    -- 传入renderNavBar,renderTitleRow
    context.navbar = 'plain'
    context.border = 'child'
    local selected, abbrN, state =
                    getArg(prefix, 'selected'),
                    getArg(prefix, 'abbr' .. listnum),
                    'uncollapsed'
    if selected ~= nil and selected == abbrN then
        state = 'uncollapsed'
    else
        state = getArg(prefix, 'state' .. listnum, 'collapsed')
    end
    context.state = state

    -- 传入renderTitleRow
    -- context.titleEqGroup=true
    context.notNeedTitlegroup = true
    context.titlestyle = table.concat({
        (getArg(prefix, 'basestyle', '')),
        (getArg(prefix, 'groupstyle', '')),
        (getArg(prefix, 'secttitlestyle', '')),
        (getArg(prefix, 'group' .. listnum .. 'style', '')),
        (getArg(prefix, 'sect' .. listnum .. 'titlestyle', ''))
    }, ';')
    context.title = (getArg(prefix, 'group' .. listnum, '')) ..
                    (getArg(prefix, 'sect' .. listnum, '')) ..
                    (getArg(prefix, 'section' .. listnum, ''))

    -- 传入renderListRow
    context.contentEqList = true
    context.notNeedGroup = true
    context.liststyle = table.concat({
        (getArg(prefix, 'liststyle', '')),
        (getArg(prefix, 'contentstyle', '')),
        (getArg(prefix, 'list' .. listnum .. 'style', '')),
        (getArg(prefix, 'content' .. listnum .. 'style', ''))
    }, ';')
    local totalColspan = 2 -- title,above,below
    local totalRowspan = 1 -- image,imageleft

    -- 传入image
    local imageLeft, image = getArg(prefix, "imageleft" .. listnum, nil,context, 'imageleft'),
                             getArg(prefix,"image" ..listnum,nil, context,'image')
    if imageLeft then
        totalColspan = totalColspan + 1
        context.imageleft = imageLeft
    else
        context.notImageLeftCell = true -- CollapsibleListRow 适配
    end
    if image then
        totalColspan = totalColspan + 1
        context.image = image
    else
        context.notImageCell = true -- CollapsibleListRow 适配
    end

    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    context.splitRowcolspan = totalColspan
    context.lockEvenOdd = true -- CollapsibleListRow 适配

    debugLog(
        'SmallNavboxInCollapsibleListRow Implement',
        'listnum=' .. listnum,
        context)

    -- start
    local rootTable2 = createNavTableHeader(context)
    renderTitleRow(rootTable2, context)
    -- only 1 list
    local otherListFunction
    local listHaveChild = checkHaveChild(prefix, 'list' .. listnum)
    local contentHaveChild = context.contentEqList and
                                 checkHaveChild(prefix, 'content' .. listnum)
    if (listHaveChild or contentHaveChild) and level <= Limit.child then
        local listKeyName = 'list'
        if contentHaveChild then listKeyName = 'content' end
        local childContext = NavboxContext.new(
                prefix ..(prefix == '' and '' or '-') ..listKeyName .. listnum,
                level + 1,
                getValidType(
                    getArg(prefix ..(prefix == '' and '' or'-') ..listKeyName ..listnum, 'type'),
                    NavType.V),
                context
        )
        debugLog('SmallNavboxInCollapsibleListRow NewChild', childContext)
        otherListFunction = _renderColRow_FunctionBuilder(
                nil,
                childContext,
                p.renderNavTable)
    end
    context.noEvenOddStyle = true
    _renderListRow(rootTable2, context, otherListFunction)
    context.noEvenOddStyle = nil
    debugLog('_renderSmallNavboxInCollapsibleListRow End')
    return rootTable2:allDone()
end

---折叠行具体实现
local function renderCollapsibleListRow(rootTable, context)
    local prefix, level = context.prefix, context.level
    debugLog('renderCollapsibleListRow', {
            ['prefix'] = prefix},
            {['context'] = context})
    context.notNeedGroup = true
    local listnum = context.listnum
    local context_function
    if getArg(prefix, 'group' .. listnum) or 
       getArg(prefix, 'sect' .. listnum) or
       getArg(prefix, 'section' .. listnum) 
    then
        local grandChild_context = NavboxContext.new(prefix, level)
        grandChild_context.notNeedGroup = true
        grandChild_context.listpadding = getArg(prefix, 'listpadding')
        grandChild_context.listnum = listnum
        context_function = _renderColRow_FunctionBuilder(
                            rootTable,
                            grandChild_context,
                            _renderSmallNavboxInCollapsibleListRow)
        debugLog('renderCollapsibleListRow function generate', {
            ['context'] = context,
            ['grandChild_context'] = grandChild_context
        })
    end
    context.noEvenOddStyle = true
    debugLog('renderCollapsibleListRow renderListRow', {['context'] = context})
    _renderListRow(rootTable, context, context_function)
    context.noEvenOddStyle = nil
    debugLog('renderCollapsibleListRow End')
end

---------------------------------------------------------------
--
--   Tracking categories
--

-- 没有使用水平列表的导航框
local function needsHorizontalLists(context)
    local prefix = context.prefix
    local border, listclass, bodyclass, tracking  =
                context.border or getArg(prefix, 'border'),
                context.listclass or getArg(prefix, 'listclass'),
                context.bodyclass or getArg(prefix, 'bodyclass'),
                getArg(prefix, 'tracking')
    debugLog('needsHorizontalLists',
        {['border']=border,['listclass']=listclass,['bodyclass']=bodyclass,['tracking']=tracking})
    if borderIsChild(border) or tracking == 'no' then
        return false
    end

    local listClasses = {
     'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap',
     'hlist vcard', 'vcard hlist', 'hlist vevent', 'hlist hlist-pipe',
     'hlist hlist-hyphen'}
    for i, cls in ipairs(listClasses) do
        if listclass == cls or bodyclass == cls then return false end
    end
    return true
end

-- 使用背景颜色的导航框
local function hasBackgroundColors(context)
    local prefix = context.prefix
    local titlestyle, groupstyle, basestyle =
        context.titlestyle or getArg(prefix, 'titlestyle'),
        context.groupstyle or getArg(prefix, 'groupstyle'),
        context.basestyle or getArg(prefix, 'basestyle')
    debugLog('hasBackgroundColors',
        {['titlestyle']=titlestyle,['groupstyle']=groupstyle,['basestyle']=basestyle})
    return
        mw.ustring.match(titlestyle or '', 'background') or
        mw.ustring.match(groupstyle or '', 'background') or
        mw.ustring.match(basestyle or '', 'background')
end

-- name參數和實際不同的導航框
local function argNameAndRealTitleAreDifferent(context)
    local prefix = context.prefix
    local border, name, tracking =
                getArg(prefix, 'border', nil, context),
                getArg(prefix, 'name', nil, context),
                getArg(prefix, 'tracking')
    debugLog('argNameAndRealTitleAreDifferent',
        {['border']=border,['name']=name,['tracking']=tracking})
    if borderIsChild(border) or tracking == 'no' then
        return false
    end

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

local catCheckList = {
    ['needsHorizontalLists'] = {
        ['catkey'] = 'needsHorizontalLists',
        ['catCheckFunc'] = needsHorizontalLists,
        ['catName'] = '没有使用水平列表的导航框'
    },
    ['hasBackgroundColors'] = {
        ['catkey'] = 'hasBackgroundColors',
        ['catCheckFunc'] = hasBackgroundColors,
        ['catName'] = '使用背景颜色的导航框'
    },
    ['argNameAndRealTitleAreDifferent'] = {
        ['catkey'] = 'argNameAndRealTitleAreDifferent',
        ['catCheckFunc'] = argNameAndRealTitleAreDifferent,
        ['catName'] = 'name參數和實際不同的導航框'
    }
}

-- 检查并获得需要的分类
local function getTrackingCategories(context)
    local cats = context['catList'] or {}
    for catkey, checkObj in pairs(catCheckList) do
        if checkObj['catCheckFunc'](context) then
            table.insert(cats, checkObj['catkey'])
        end
    end
    debugLog('getTrackingCategories',{['level']=context.level,['catList']=cats})
    context['catList'] = cats
end

-- 生成分类
local function renderTrackingCategories(builder, context)
    local title = mw.title.getCurrentTitle()
    if DEBUG == false then
        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 --
    end

    getTrackingCategories(context)
    debugLog('renderTrackingCategories',{['level']=context.level})
    local catList = {}
    local workContext = context
    repeat
		local workCatList = workContext['catList'] or {}
        for _, catKey in ipairs(workCatList) do
            table.insert(catList, catKey)
        end
        debugLog('renderTrackingCategories.1',catList)
        catList = removeDump(catList)
        if workContext.level == 1 then
            debugLog('renderTrackingCategories.2',catList)
            workContext['globalCatList']=catList
        end
        workContext = workContext.parent
    until (workContext == NavboxContext.END )
    
    if context.level ==1 then
        local catList=context['globalCatList'] or {}
        for i, cat in ipairs(catList) do
            builder:wikitext('[[Category:' .. catCheckList[cat]['catName'] ..']]')
        end
    end
end

---------------------------------------------------------------
--
--  SubType Implement
--

---水平式
local function renderHorizontalTable(context)
    debugLog('render Horizontal NavTable', context)
    local prefix, level = context.prefix, context.level

    local rootTable = createNavTableHeader(context)

    local listnums = getListnum(prefix, Limit.horizontal.list)

    local totalColspan = 2 -- title,above,below
    local totalRowspan = #listnums -- image,imageleft
    if getArg(prefix, "imageleft", nil, context) then
        totalColspan = totalColspan + 1
    end
    if getArg(prefix, "image", nil, context) then
        totalColspan = totalColspan + 1
    end
    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    -- context.splitRowcolspan = totalColspan

    renderTitleRow(rootTable, context)
    renderAboveRow(rootTable, context)

    if listnums == nil or #listnums == 0 then -- 没有list的话,只有col
        debugLog('render Horizontal NavTable,no list', {listnums})
        context.listnum = 1
        renderColRow(rootTable, context)
        -- context.notNeedImage=true
        context.splitRowcolspan = totalColspan
    else
        debugLog('render Horizontal NavTable,have list with col', {listnums})
        for i, listnum in ipairs(listnums) do
            context.listnum = listnum
            if listnum == 1 then
                -- 一行Col
                renderColRow(rootTable, context)
                context.notNeedImage = true
                context.splitRowcolspan = totalColspan
            else
                context.notNeedImage = nil
            end
            _renderListRow(rootTable, context)
        end
    end
    renderBelowRow(rootTable, context)

    renderTrackingCategories(rootTable, context)
    debugLog('render Horizontal NavTable End')
    return rootTable
end

---垂直式
local function renderVerticalTable(context)
    debugLog('render Vertical NavTable', context)
    local prefix, level = context.prefix, context.level

    local rootTable = createNavTableHeader(context)

    local listnums = getListnum(prefix, Limit.vertical)

    local totalColspan = 2 -- title,above,below
    local totalRowspan = #listnums -- image,imageleft
    if getArg(prefix, "imageleft", nil, context) then
        totalColspan = totalColspan + 1
    end
    if getArg(prefix, "image", nil, context) then
        totalColspan = totalColspan + 1
    end
    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    -- context.splitRowcolspan = totalColspan

    renderTitleRow(rootTable, context)
    renderAboveRow(rootTable, context)
    for i, listnum in ipairs(listnums) do
        context.listnum = listnum
        renderListRow(rootTable, context)
    end
    renderBelowRow(rootTable, context)

    renderTrackingCategories(rootTable, context)
    debugLog('render Vertical NavTable End')
    return rootTable
end

---垂直折叠式
local function renderVerticalCollapsibleTable(context)
    debugLog('render VerticalCollapsible NavTable', context)
    local prefix, level = context.prefix, context.level

    local rootTable = createNavTableHeader(context)

    local listnums = getListnum(prefix, Limit.vertical,
        ( --[[context.contentEqList or ]] true) -- VerticalCollapsibleTable 的 Content适配
    )

    local totalColspan = 2 -- title,above,below
    local totalRowspan = #listnums -- image,imageleft
    if getArg(prefix, "imageleft", nil, context) then
        totalColspan = totalColspan + 1
    end
    if getArg(prefix, "image", nil, context) then
        totalColspan = totalColspan + 1
    end
    context.totalColspan = totalColspan
    context.totalRowspan = totalRowspan
    -- context.splitRowcolspan = totalColspan

    renderTitleRow(rootTable, context)
    renderAboveRow(rootTable, context)
    for i, listnum in ipairs(listnums) do
        context.listnum = listnum
        renderCollapsibleListRow(rootTable, context)
    end
    renderBelowRow(rootTable, context)

    renderTrackingCategories(rootTable, context)
    debugLog('render VerticalCollapsible NavTable End')
    return rootTable
end

---Type Selector
function p.renderNavTable(context)
    local navtype = context.type
    debugLog('render NavTable')
    debugLog('Type=' .. navtype)
    local result
    if navtype == NavType.H then
        result = renderHorizontalTable(context)
    elseif navtype == NavType.VC then
        result = renderVerticalCollapsibleTable(context)
    else
        result = renderVerticalTable(context)
    end
    debugLog('render NavTable End')
    return result
end

-- Main Funtion
function p._navbox(context)
    debugLog('Navbox mainfuntion', context)
    local prefix, level = context.prefix, context.level

    local rootTable
    --[[
    适配list内再嵌套单独Navbox的情况,部分Navbox可以单独或者再嵌入使用
    原生的Navbox的各种实现机制就是Navbox嵌套
    ]]
    local border = trim(getArg(prefix, 'border') or getArg(prefix, '1') or '')
    debugLog("_Navbox.border", border)
    if border == 'none' then
        rootTable = p.renderNavTable(context):allDone()
    elseif borderIsChild(border) then
        -- Navbox的值段直接嵌套单独Navbox的情况
        rootTable = mw.html.create()
        rootTable
            :wikitext('</div>')
            :node(p.renderNavTable(context):allDone())
            :wikitext('<div>')
    else
        rootTable = mw.html.create('table')
        rootTable
            :attr('cellspacing', 0)
            :addClass('navbox')
            :css('border-spacing', 0)
            :cssText(getArg(prefix, 'bodystyle'))
            :cssText(getArg(prefix, 'style'))
                :tag('tr')
                    :tag('td')
                    :css('padding', '2px')
                    :node(p.renderNavTable(context):allDone())
    end

    debugLog('Navbox mainfuntion End')
    return rootTable
end

function p.navbox(frame)
    if not getArgs then getArgs = require('Module:Arguments').getArgs end

    local modelArgs = getArgs(frame, {frameOnly = true})
    DEBUG = modelArgs['DEBUG'] or DEBUG
    MainTemplateName = modelArgs['MainTemplateName'] or MainTemplateName
    debugLog('Navbox start')

    args = getArgs(frame, {wrappers = MainTemplateName, trim = true})
    debugLog('getArgs done,', args)

    local prefix, level = "", 1
    local navType = getValidType(
                getArg(prefix, 'type') or modelArgs['type'], NavType.V)
    DEBUG = modelArgs['DEBUG'] or DEBUG
    -- Read the arguments in the order they'll be output in, to make references number in the right order.
    p.shakeArgs(prefix, level, navType)

    local L0Context = NavboxContext.new(prefix, level, navType)
    local rootNode = p._navbox(L0Context)
    debugLog('rootnode build done, Navbox end')

    return tostring(rootNode:allDone())
end

return p