2023年政策修订增补工作正在进行中,欢迎参与!
Module:Clade
跳转到导航
跳转到搜索
本模板用于绘制树。
简易语法
只传入匿名参数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 * 可以写多个树 * …… }}
树1
枝1
枝1-1
叶子1-1-1(可以写wikitext,但不能跨行)
枝1-2
叶1-2-1(可以嵌套单行模板)
枝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=枝c |4=叶子3 }}
根
枝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