模組:NavboxV2
外观
![]() | 此模块被引用於約34,000個頁面。 為了避免造成大規模的影響,所有對此模块的編輯應先於沙盒或測試樣例上測試。 測試後無誤的版本可以一次性地加入此模块中,但是修改前請務必於討論頁發起討論。 模板引用數量會自動更新。 |
![]() | 此模板不能在维基百科的移动视图中显示,只能在桌面版显示。请阅读文档以获得解释。 |
![]() | 此模块使用Lua语言: |
![]() | 本模块使用以下模板样式: |
这是{{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}}系列模板基本兼容。但新增部分参数填入:
type
:Navbox的类型,对应值为vertical
(对应{{Navbox}})、horizontal
(对应{{Navbox with columns}})、vertical_collapsible
(对应{{Navbox with collapsible groups}}),默认值为vertical
。border
:Navbox的隐藏参数,用于控制Navbox的边框机制,来使子Navbox能被嵌入到父Navbox的值字段(例如list
、col
等)中,实际对应{{Navbox subgroup}}或{{Navbox|child}}的实现机制。对应值为child
、subgroup
任一个。
- 在本模板添加子Navbox层时,必须传入这两个参数,这是本模板区分是否存在子Navbox层的依赖。本模板首层Navbox层无需添加
border
,按需添加type
。
- 在本模板添加子Navbox层时,必须传入这两个参数,这是本模板区分是否存在子Navbox层的依赖。本模板首层Navbox层无需添加
removeGroupPadding
:用于区别{{Navbox|child}}和{{Navbox subgroup}},后者在Groupn字段的单元格增加一组padding的配置,适用于子Navbox层。任意值,存在则可,为移除该padding配置(对应{{Navbox|child}})。- {{Navbox subgroup}}是{{Navbox generic}}改型为{{Navbox}}后的遗漏产物,在此变更后等效于{{Navbox|child}},该参数不再使用。
方便复制的代码:
| <list/content>-type = vertical | horizontal | vertical_collapsible | <list/content>-border = child| subgroup
自{{Navbox}}系列模板转换
将原有嵌入{{Navbox}}系列模板的值字段listn
(其他类同)改为listn-
,并作为相应嵌套子Navbox模板的参数的前缀来加入,使这些模板嵌套转换为扁平化的一层模板参数。
{{Navbox}}系列 | 本模板 |
---|---|
{{Navbox
|name = Navbox/doc
|state = expanded
|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 = expanded
|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检查,确认转换后能与原来的样式、功能基本一致,才应用转换。如果出现问题,请保留案例并联系本模板维护编辑协助处理,或者放弃。
虽然可以在值字段(例如list
、col
等)重新嵌入{{Navbox}}系列模板,但这和原有做法一样,失去了本模板降低解析器限制的作用,不建议这样做。
--
-- This module will implement {{Navbox}}
--
local p = {}
local navbar = require('Module:Navbar')._navbar
local NavboxContext = require('模块:沙盒/Cwek/NavboxV2/Context')
local getArgs -- lazily initialized
local args
--Constant Define
local Limit={
vertical=35
,horizontal={
col=20
,list=6
}
,vertical_collapsible=11
,child=10
}
local NavType={
V="vertical" --垂直,list
,H="horizontal" --水平,col
,VC="vertical_collapsible" --折叠垂直
}
--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
---Functional Function
local getArg=function( _a , _b)
local prefix= ( _b==nil and _a~=nil ) and _a or ""
local key= _b==nil and _a or _b
return args[ prefix .. (prefix=="" and "" or "-") ..key ]
end
local checkHaveChild=function(prefix,valuekey)
if getArg(prefix..valuekey..'type') and getArg(prefix..valuekey..'border') then
return true
end
return false
end
---Element Render
function createNavTableHeader(context)
local rootTable=
mw.html.create('table')
:attr('cellspacing', 0)
:addClass('nowraplinks')
:addClass(getArg(prefix,"bodyclass"))
:css('border-spacing', 0)
:cssText(getArg(prefix,"innerstyle"))
local state=getArg(prefix,"state")
if getArg(prefix,"title") and (state ~= 'plain' and state ~= 'off') then
rootTable
:addClass('collapsible')
:addClass(state or 'autocollapse')
end
local border=getArg(prefix,"border")
if border == 'subgroup' or border == 'child' or border == 'none' then
rootTable
:addClass('navbox-subgroup')
:cssText(getArg(prefix,"bodystyle"))
:cssText(getArg(prefix,"style"))
else -- regular navobx - bodystyle and style will be applied to the wrapper table
rootTable
:addClass('navbox-inner')
:css('background', 'transparent')
:css('color', 'inherit')
end
return rootTable
end
function renderNavBar(titleCell,context)
local prefix = context.prefix
local navbar = getArg(prefix,"navbar")
local state = getArg(prefix,"state")
local border = getArg(prefix,"border")
local name = getArg(prefix,"name")
-- 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() == 'Template:Navbox' 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(navbar{
args.name,
mini = 1,
fontstyle = (args.basestyle or '') .. ';' .. (args.titlestyle or '') .. ';background:none transparent;border:none;'
})
end
-- Render the spacer div.
if spacerSide then
titleCell
:tag('span')
:css('float', spacerSide)
:css('width', '8em')
:css('font-size', '80%')
:css('margin-' .. (spacerSide == 'left' and 'right' or 'left'), '0.5em')
:wikitext(' ')
end
end
---
function renderSplitRow(rootTable,context)
if context.needAddSplitRow then
rootTable
:tag('tr')
:css('height', '2px')
:tag('td')
:attr('colspan',2)
end
context.needAddSplitRow=true
end
function renderTitleRow(rootTable,context)
local prefix=context.prefix
local title=getArg(prefix,"title")
if not title then return end
renderSplitRow(rootTable)
local titleColspan=context.TotalColspan
local titlegroup=getArg(prefix,"titlegroup")
local titleRow=mw.html.create('tr')
if titlegroup then
titleRow
:tag('th')
:attr('scope', 'row')
:addClass('navbox-group')
:addClass(getArg(prefix,"titlegroupclass"))
:cssText(getArg(prefix,"basestyle"))
:cssText(getArg(prefix,"groupstyle"))
:cssText(getArg(prefix,"titlegroupstyle"))
:wikitext(getArg(prefix,"titlegroup"))
end
local titleCell = titleRow:tag('th'):attr('scope', 'col')
if titlegroup then
titleCell
:css('border-left', '2px solid #fdfdfd')
:css('width', '100%')
titleColspan = titleColspan - 1
end
titleCell
:cssText(getArg(prefix,"basestyle"))
:cssText(getArg(prefix,"titlestyle"))
:addClass('navbox-title')
:attr('colspan', titleColspan)
renderNavBar(titleCell)
titleCell
:tag('div')
:addClass(getArg(prefix,"titleclass"))
:css('font-size', '110%')
:wikitext(addNewline(title))
rootTable:node(titleRow)
end
function renderAboveRow(rootTable,context)
local prefix=context.prefix
local above=getArg(prefix,"above")
if not above then return end
renderSplitRow(rootTable)
local Colspan=context.TotalColspan
local AboveRow=mw.html.create('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))
rootTable:node(AboveRow)
end
function renderBelowRow(rootTable,context)
local prefix=context.prefix
local below=getArg(prefix,"below")
if not below then return end
renderSplitRow(rootTable)
local Colspan=context.TotalColspan
local BelowRow=mw.html.create('tr')
BelowRowmw
: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))
rootTable:node(BelowRowmw)
end
function _renderListRow(rootTable,context)
renderSplitRow(rootTable)
local prefix,level=context.prefix,context.level
local listnum=listnum or context.listnum
local isFirst = (listnum==1)
local isOdd = (listnum % 2) == 1
local ImageRowspan = 2 * context.Totalrowspan - 1
local listRow=rootTable:tag('tr')
local imageLeftCell
,imageCell
,groupCell
,listCell
--image
if isFirst and (not context.notNeedImage) then
local imageLeft=context.imageLeft or getArg(prefix,"imageleft")
local image=context.image or getArg(prefix,"image")
if imageLeft then
imageLeftCell=mw.html.create('td')
:addClass('navbox-image')
:addClass(getArg(prefix,"imageclass"))
:css('width', '0%')
:css('padding', '0px 2px 0px 0px')
:cssText(getArg(prefix,"imageleftstyle"))
:attr('rowspan', ImageRowspan)
:tag('div')
:wikitext(addNewline(imageLeft))
listRow:node(imageLeftCell)
--imageLeftCell=nil
end
if image then
imageCell=mw.html.create('td')
:addClass('navbox-image')
:addClass(getArg(prefix,"imageclass"))
:css('width', '0%')
:css('padding', '0px 2px 0px 0px')
:cssText(getArg(prefix,"imagestyle"))
:attr('rowspan', ImageRowspan)
:tag('div')
:wikitext(addNewline(image))
end
end
--group
local group=getArg(prefix,"group"..listnum)
local groupwidth= ( group and getArg(prefix,"groupwidth") ) or nil
if group --[[and (not haveColList)]]
then
groupCell=listRow:tag('th') --=mw.html.create('th')
:attr('scope', 'row')
:addClass('navbox-group')
:addClass(getArg(prefix,"groupclass"))
:cssText(getArg(prefix,"basestyle"))
:cssText(getArg(prefix,"groupstyle"))
:cssText(getArg(prefix,'group' .. listnum .. 'style'))
:wikitext(group)
if groupwidth then
groupCell:css('width', groupwidth )
end
--listRow:node(groupCell)
--groupCell=nil
end
--list
do
listCell=listRow:tag('td') --mw.html.create('td')
if group --[[and (not haveColList) ]]
then
listCell
:css('text-align', 'left')
:css('border-left-width', '2px')
:css('border-left-style', 'solid')
else
listCell:attr('colspan', 2)
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 list1padding = haveColList and "0px;" or getArg(prefix,"list1padding")
local listNstyle= ( haveColList and listnum==1 ) and
"background:transparent;color:inherit;" or
getArg(prefix,'list' .. listnum .. 'style')
listCell
:css('padding', '0px')
:cssText(getArg(prefix,"liststyle"))
:cssText(isOdd and getArg(prefix,"evenstyle") or getArg(prefix,"oddstyle"))
:cssText(listNstyle)
:addClass('navbox-list')
:addClass('navbox-' .. evenOdd)
:addClass(getArg(prefix,"listclass"))
:tag('div')
:css('padding', (isFirst and list1padding) or getArg(prefix,"listpadding") or '0em 0.25em')
--[[if haveColList then
listCell:node(renderColRow(prefix,context))
else]]if ( getArg(prefix,'list'..listnum..'border') and getArg(prefix,'list'..listnum..'type') ) and level<= Limit.child then
listCell:node(p.renderNavTable('list'..listnum,level+1))
else
listCell:wikitext(addNewline(getArg(prefix,'list' .. listnum)))
end
end
--end
--listRow:node(listCell)
if imageCell then listRow:node(imageCell) end
end
function renderListRow(rootTable,context)
_renderListRow(rootTable,context)
end
function renderColRow(rootTable,context)
--TODO
end
function _renderSmallNavboxInCollapsibleListRow(rootTable,context,listnum)
--TODO
end
function renderCollapsibleListRow(rootTable,context,listnum)
--TODO
end
---SubType Implement
function renderHorizontalTable(context)
local prefix , level = context.prefix, context.level
local rootTable = createNavTableHeader(context)
local listnums={}--,colnums={}
for k, v in pairs(args) do
local listnum = ('' .. k):match('^'..prefix..(prefix=="" and "" or "-")..'list(%d+)$')
--local colhead_num = ('' .. k):match('^'..prefix..(prefix=="" and "" or "-")..'colheader(%d+)$')
--local colbody_num = ('' .. k):match('^'..prefix..(prefix=="" and "" or "-")..'col(%d+)$')
--local colnum = colhead_num or colbody_num
if listnum and tonumber(listnum)<=Limit.horizontal.list then
table.insert(listnums, tonumber(listnum))
end
--[[
if colnum and tonumber(colnum)<=Limit.horizontal.col then
table.insert(colnums, tonumber(colnum))
end
]]
end
table.sort(listnums)
local Totalcolspan=2 --title,above,below
local Totalrowspan=#listnums --image,imageleft
if getArg(prefix,"imageleft") then Totalcolspan =Totalcolspan + 1 end
if getArg(prefix,"image") then Totalcolspan =Totalcolspan + 1 end
context.TotalColspan = Totalcolspan
context.Totalrowspan = Totalrowspan
renderTitleRow(rootTable,context)
renderAboveRow(rootTable,context)
renderColRow(rootTable,context)
for i,listnum in ipairs(listnums) do
context.listnum=listnum
renderListRow(rootTable,context)
end
renderBelowRow(rootTable,context)
return rootTable
end
function renderVerticalTable(context)
local prefix , level = context.prefix , context.level
local rootTable = createNavTableHeader(context)
local listnums={}
for k, v in pairs(args) do
local listnum = ('' .. k):match('^'..prefix..(prefix=="" and "" or "-")..'list(%d+)$')
if listnum and listnum<=Limit.vertical then
table.insert(listnums, tonumber(listnum))
end
end
table.sort(listnums)
local Totalcolspan=2 --title,above,below
local Totalrowspan=#listnums --image,imageleft
if getArg(prefix,"imageleft") then Totalcolspan =Totalcolspan + 1 end
if getArg(prefix,"image") then Totalcolspan =Totalcolspan + 1 end
context.TotalColspan = Totalcolspan
context.Totalrowspan = Totalrowspan
renderTitleRow(rootTable,context)
renderAboveRow(rootTable,context)
for i,listnum in ipairs(listnums) do
context.listnum=listnum
renderListRow(rootTable,context)
end
renderBelowRow(rootTable,context)
return rootTable
end
function renderVerticalCollapsibleTable(context)
local prefix , level = context.prefix , context.level
local rootTable = createNavTableHeader(context)
local listnums={}
for k, v in pairs(args) do
local listnum = ('' .. k):match('^'..prefix..(prefix=="" and "" or "-")..'list(%d+)$')
if listnum and listnum<=Limit.vertical then
table.insert(listnums, tonumber(listnum))
end
end
table.sort(listnums)
local Totalcolspan=2 --title,above,below
local Totalrowspan=#listnums --image,imageleft
if getArg(prefix,"imageleft") then Totalcolspan =Totalcolspan + 1 end
if getArg(prefix,"image") then Totalcolspan =Totalcolspan + 1 end
context.TotalColspan = Totalcolspan
context.Totalrowspan = Totalrowspan
renderTitleRow(rootTable,context)
renderAboveRow(rootTable,context)
for i,listnum in ipairs(listnums) do
context.listnum=listnum
renderCollapsibleListRow(rootTable,context)
end
renderBelowRow(rootTable,context)
return rootTable
end
---Type Selector
function _renderNavTable(context)
local NavType = context.type
if NavType==NavType.H then
return renderHorizontalTable(context)
elseif NavType==NavType.V then
return renderVerticalTable(context)
elseif NavType==NavType.VC then
return renderVerticalCollapsibleTable(context)
end
return mw.html.create("div")
end
--Main Funtion
function p._navbox(conctext)
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(_renderNavTable(conctext))
return rootTable
end
function p.navbox(frame)
if not getArgs then
getArgs = require('Module:Arguments').getArgs
end
args = getArgs(frame, {wrappers = 'Template:Navbox'})
local prefix, level = "", 1
local NavType = getArg() or NavType.V
-- 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)
return tostring(rootNode)
end