模块:NavboxV2

本页使用了标题或全文手工转换,现处于中国大陆简体模式
求闻百科,共笔求闻

这是模板链接:{{NavboxV2}}的Lua实现代码。

简介

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

改写自模块:Navbox

设计用途

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

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

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

效果

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

参数

模板链接:{{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}}系列模板,但这和原有做法一样,失去了本模板降低解析器限制的作用,不建议这样做。

上述文档内容嵌入自Module:NavboxV2/doc编辑 | 历史
编者可以在本模块的沙盒创建 | 镜像和测试样例创建页面进行实验。
请将模块自身所属的分类添加在文档中。本模块的子页面
--
-- This module will implement {{Navbox}}
--

local p = {}

--Context start
local _NavboxContext={}
local NavboxContextNewObj=function(_prefix,_level,_type)
    return {
         prefix = _prefix
        ,level = _level
        ,type = _type
        ,_Context={}
    }
end

local NavboxContextMetaFunction={
     __index=function(_obj,key)
      local _key = tostring(key)
      if _key=='prefix' or _key =='level' or _key =='type' 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' 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)
        for k,v in pairs(_obj._Context) do
            table.insert(output,k..'='..v)
        end
        return 'context:{\n'..table.concat(output,",\n")..'\n}'
    end
}

_NavboxContext.new=function(prefix,level,_type)
    local _prefix = prefix or ""
    local _level = level or 1

    local newobj=NavboxContextNewObj(_prefix,_level,_type or '')
    setmetatable(newobj,NavboxContextMetaFunction)
    --[[function newobj:remove(key)
        mw.log(self)
        NavboxContext_Remove(self,key)
    end]]
    return newobj
end
--Context end

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

--全局性参数锚
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 function trim(s)
    return (mw.ustring.gsub(s, "^%s*(.-)%s*$", "%1"))
end

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

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

local function tableToString(_table)
    local outputs={}
    if 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

local debugLog=function(...)
    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 function getListnum(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
        rootTable
            :addClass('collapsible')
            :addClass( state or 'autocollapse')
    end

    if border == 'subgroup' or border == 'child' 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)
    else
    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
            (border == 'subgroup' or border == 'child' 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(
            {
                  getArg(prefix,'basestyle',nil,context) or ''
                 ,(titlestyle or '')
                 ,'background:none transparent;border:none'
            }
            ,';')
        })
    end

    -- Render the spacer div.
    if spacerSide then
        titleCell
            :tag('span')
                :css('float', spacerSide)
                :css('width', '8em')
                :css('font-size', '80%')
                :css('margin-' .. (spacerSide == 'left' and 'right' or 'left'), '0.5em')
                :wikitext('&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')--[[,{['parent']=listRow}]]
             :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 )
    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(getArg(prefix,"groupstyle"))
            :cssText(getArg(prefix,'group' .. listnum .. 'style'))
        local tempEle=groupCell
        if (borderIsChild(getArg(prefix,"border"))) and (getArg(prefix,'type')==NavType.V) and (not removeGroupPadding) 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
                 )
            )
            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)
                    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))
                    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(colN))
                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)
                    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))
        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 =
             context.border    or getArg(prefix,'border')
            ,context.listclass or getArg(prefix,'listclass')
            ,context.bodyclass or getArg(prefix,'bodyclass')

    if borderIsChild(border)
        or getArg(prefix,'tracking') == 'no' then
        return false
    end

    local listClasses = {'plainlist', 'hlist', 'hlist hnum', 'hlist hwrap', 'hlist vcard', 'vcard hlist', 'hlist vevent', 'hlist hlist-pipe'}
    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')
    return mw.ustring.match(titlestyle or '','background') or
           mw.ustring.match(groupstyle or '','background') or
           mw.ustring.match(basestyle  or '','background')
end

local function argNameAndRealTitleAreDifferent(context)
    local prefix = context.prefix
    local border ,name =
             getArg(prefix,'border',nil,context)
            ,getArg(prefix,'name',nil,context)
    if borderIsChild(border)
        or getArg(prefix,'tracking') == 'no' then
        return false
    end

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

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

local function renderTrackingCategories(builder,context)
    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 i, cat in ipairs(getTrackingCategories(context)) do
        builder:wikitext('[[Category:' .. cat .. ']]')
    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 = 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())
    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