本模板用于绘制树。
只传入匿名参数1以使用简易语法。传入其他任何参数将被认为是复杂语法。
在简易语法中,每行写一个结点,以*区分层级,类似wikitext的无序列表。
{{clade|1=
树1
* 枝1
** 枝1-1
*** 叶子1-1-1(可以写''wikitext'',但'''不能'''跨行)
** 枝1-2
*** 叶1-2-1(可以嵌套{{color|#161|单行模板}})
** 枝1-3
*** 枝1-3-1
**** 叶1-3-1-1
*** 枝1-3-2
**** 叶1-2-2-1
* 枝2
** 叶2-1
树2
* 可以写多个树
* ……
}}
root:根节点内容labelx:第x个叶子节点上的分支标签,label1、label2以此类推。x:第x个叶子节点的内容。参数都不是必须,可以留空,会自动根据输入的情况生成。
使用所有参数:
{{clade
|root=根
|label1=枝a
|1=叶子1
|label2=枝b
|2=叶子2
|label3=枝c
|3=叶子3
|label4=枝d
|4=叶子4
}}
| 枝a | 叶子1 |
| 枝b | 叶子2 |
| 枝c | 叶子3 |
| 枝d | 叶子4 |
使用部分参数:
{{clade
|1=叶子1
|2=叶子2
|3=叶子3
|label4=枝d
}}
| 叶子1 | |
| 叶子2 | |
| 叶子3 | |
| 枝d | |
嵌套使用生成多级子树:
需要生成多级子树时,子树不建议输入root参数,容易出现错误。
{{clade
|root=根
|label1=枝a
|1={{clade
|label1=枝a-a
|1=叶子a-1
|label2=枝a-b
|2=叶子a-2
|label3=枝a-c
|3={{clade
|label1=枝a-c-a
|1=叶子a-c-1
|label2=枝a-c-b
|2=叶子a-c-2
}}
}}
|label2=枝b
|2=叶子2
}}
| 枝a |
| |||||||||||||||
| 枝b | 叶子2 | |||||||||||||||
local getArgs = require('Module:Arguments').getArgs
local insert = table.insert
local format = string.format
local match = string.match
local p = {}
--[[- 返回node。
node是一张表,结构为 {
label = '',
node1,
node2,
...
}
]]
local function Node(label, ...)
return {
label = label,
...
}
end
--[=[- 预处理传入的字符串,返回一个数组,每个元素为{深度, 文字}的形式,其中树根的深度为1。
例如:
preprocessLines([[
Root
* A
** A1
* B
]])
将返回
{
{1, 'Root'},
{2, 'A'},
{3, 'A1'},
{2, 'B'}
}
]=]
local function preprocessLines(raw)
local lines = {}
for rawLine in string.gmatch(raw..'\n', '([^\n]+)\n') do
insert(lines, {match(rawLine, '^%**()%s*(.-)%s*$')})
end
return lines
end
--- 给传入的node用depth个父结点包装,返回包装最外层的结点。
-- depth == 0时,返回原node。
local function wrapNode(node, depth)
local wrappedNode = node
for i = 1, depth do
wrappedNode = Node('', wrappedNode)
end
return wrappedNode
end
--[=[- 创建森林。
例如:
Forest([[
Root
* A
** A1
* B
]])
将返回
{
label = nil,
{
label = 'Root',
{
label = 'A',
{
label = 'A1'
}
},
{
label = 'B'
}
}
}
]=]
local function Forest(raw)
local lines = preprocessLines(raw)
local lineNumber = 1 -- 被多个generateBranch函数共享状态
--- 构建指定层数的树枝
-- 返回一个表,包含其树枝
local function generateBranch(depth, label)
local branch = Node(label)
local curLine = lines[lineNumber]
while curLine do
local curDepth, curText = curLine[1], curLine[2]
if curDepth <= depth then
return branch
end
-- 当前行深度 > depth
lineNumber = lineNumber + 1
insert(branch, wrapNode(generateBranch(curDepth, curText), curDepth - depth - 1))
curLine = lines[lineNumber]
end
return branch
end
return generateBranch(0)
end
--[[- 将分支(或叶子)递归地转为HTML。返回2个值:html字符串, 参数是否为叶子
node = {
label = 'A',
{
label = '1',
{
label = 'a'
}
},
{
label = '2'
},
{
label = '3'
}
}
即:
1
+-- a
A |
---+-- 2
|
+-- 3
返回
<div class="clade-branch">
<div class="clade-label">1</div>
<div class="clade-leaf">a</div>
<div class="clade-space"></div>
<div class="clade-label"></div>
<div class="clade-leaf">2</div>
<div class="clade-space"></div>
<div class="clade-label"></div>
<div class="clade-leaf">3</div>
<div class="clade-space"></div>
</div>
]]
local function branchToHtml(branch)
if not branch[1] then
return '<div class="clade-leaf">' .. branch.label .. '</div>', true
end
local htmlParts = {}
for i, subNode in ipairs(branch) do
local subHtml, subNodeIsLeaf = branchToHtml(subNode)
if subNodeIsLeaf and not branch[2] then
return subHtml, false
end
htmlParts[i] = format(
'<div class="clade-label">%s</div>%s<div class="clade-space"></div>',
subNodeIsLeaf and '' or subNode.label,
subHtml
)
end
return '<div class="clade-branch">'..table.concat(htmlParts)..'</div>', false
end
local function forestToHtml(forest)
local html = {}
for i, tree in ipairs(forest) do
html[i] = branchToHtml({tree})
end
return table.concat(html)
end
function p.fromString(raw)
return forestToHtml(Forest(raw))
end
function p.main(frame)
local args = getArgs(frame)
return p._main(args)
end
function p._main(args)
-- 版本判断
local version = 2 -- 2023-12-03前的语法为version 1,之后的新语法为version 2
for k, v in pairs(args) do
if k ~= 1 then
version = 1
break
end
end
if version == 2 then
return p.fromString(args[1])
end
local clade = mw.html.create('div'):addClass('clade-wrapper') -- 外围容器
local space = '<span class="clade-space"> </span>'
if args['root'] then
clade
:tag('div'):addClass('clade-root') -- 根节点
:tag('div'):addClass('clade-root-1'):wikitext(args['root'] or ''):done()
:tag('div'):addClass('clade-root-2 clade-space'):wikitext(' ')
end
local cladeTable = clade:tag('table'):addClass('clade-table') -- 叶子部分
local i = 1
while args[i] or args['label' .. i] do -- 循环生成。边框什么的交给CSS,懒得写判断了
cladeTable
:tag('tr')
:tag('td'):addClass('clade-label'):wikitext(args['label' .. i] or space):done()
:tag('td'):attr('rowspan', '2'):wikitext(args[i] or ""):done()
:done()
:tag('tr')
:tag('td'):addClass('clade-space'):wikitext(' ')
i = i + 1
end
return clade
end
return p