置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:Clade

猛汉♂百科,万男皆可猛的百科全书!转载请标注来源页面的网页链接,并声明引自猛汉百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [查看] [编辑] [历史] [刷新]

本模板用于绘制树。

简易语法

只传入匿名参数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
枝a-a叶子a-1
 
枝a-b叶子a-2
 
枝a-c
枝a-c-a叶子a-c-1
 
枝a-c-b叶子a-c-2
 
 
 
枝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">&nbsp;</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('&nbsp;')
    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('&nbsp;')
        i = i + 1
    end

    return clade
end


return p