WIKI公告栏
欢迎各位引航者访问卡拉彼丘WIKI,由于游客访问页面会有一定的页面旧版缓存,建议你登陆B站账号进行浏览,如果您觉得本WIKI有帮助到你,欢迎推荐给身边的引航者。
全站通知:

模块:ACandy/doc

来自卡拉彼丘WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索

这是模块:ACandy的文档页面

ACandy是一个构建HTML的Lua模块。利用Lua的语法糖和元表,ACandy提供了一个易用的方式来从Lua构建HTML。大概算是一个内部DSL。

瞄一瞄

local a = require "Module:ACandy"
local some = a.some

local example = a.Fragment {
	a.h1["#top heading heading-1"] "Hello!",
	a.div { class="container", style="margin: 0 auto;",
		a.p {
			"My name is ", a.dfn("ACandy"), ", a module for building HTML.",
			a.br,
			"Thank you for your visit.",
		},
		a.p "visitors:",
		a.ul / some.li("Alice", "Bob", "Carol", "..."),
	},
}
print(example)

输出(经过格式化,下同):

<h1 id="top" class="heading heading-1">Hello!</h1>
<div style="margin: 0 auto;" class="container">
	<p>
		My name is <dfn>ACandy</dfn>, a module for building HTML.<br>
		Thank you for your visit.
	</p>
	<p>visitors:</p>
	<ul>
		<li>Alice</li>
		<li>Bob</li>
		<li>Carol</li>
		<li>...</li>
	</ul>
</div>

导入

local a = require("Module:ACandy")

建议使用a来重命名acandy,这是因为:

  • a是ACandy的首字母;
  • a很短,打起来方便;
  • a.xxx可以理解为英语的“一个xxx”。

下文的a均指代本模块。

创建元素

一个基本的例子如下:

local elem = a.p {
	class="my-paragraph", style="color: #114514;",
	"This sentence is inside a ", a.code("<p>"), " element.",
}
print(elem)

表的键值对和序列分别表示元素的属性和子结点,正如a.p那样。若仅有一个子结点且不需要设置属性,可以直接将该结点作为函数参数,所以a.code("...")a.code({ "..." })是等价的。

该代码的输出,格式化后(下同)如下。

<p class="my-paragraph" style="color: #114514;">
	This sentence is inside a <code>&lt;p&gt;</code> element.
</p>

对于HTML元素,a.xxx是不区分大小写的,因此a.diva.Diva.DIV……是同一个值,它们都将变成<div></div>。而对于其他元素,a.xxx 是大小写敏感的。

属性

通过表的键值对为元素提供属性。其中,键必须是合法的XML字符串(目前模块仅支持ASCII字符);值可以是以下内容:

  • nilfalse表示没有此属性;
  • true表示此为布尔值属性,例如,a.script { async=true }表示<script async></script>
  • 其余值,将会对其进行tostring,并转义其中的< > & "

子结点

通过表的序列部分为元素提供子结点。除nil之外的值均可作为子结点。

元素、字符串、数字、布尔值等后文没有提到的值

在元素字符串化时,对这些值尝试tostring,并转义其中的< > &。如果不期望自动的转义,可以将内容放在a.Raw

在下面这个例子中,我们将三个元素(<p>)作为<article>的子结点,并分别将字符串、数字、布尔值作为<p>的元素。结果显而易见。

local elem = a.article {
	a.p "Lorem ipsum...",  -- or `a.p { "Lorem ipsum..." }`
	a.p(2),  -- or `a.p { 2 }`
	a.p(true),  -- or `a.p { true }`
}
print(elem)
<article>
	<p>Lorem ipsum...</p>
	<p>2</p>
	<p>true</p>
</article>

在元素字符串化时,表可能被当作序列,ACandy会递归地对序列中的元素尝试字符串化。

以下表将被视为序列:

  • 未设置元表的表,如{ 1, 2, 3 }
  • a.Fragment返回的表,如a.Fragment { 1, 2, 3 }
  • 元表的"__acandy_fragment_like"字段为true的表,例如,可通过getmetatable(val).__acandy_fragment_like = true使val在字符串化时被视作序列。

除此之外的表(如a.p { 1, 2, 3 }返回的表)会被直接通过tostring转换为字符串,未定义__tostring的表会引发错误。

local parts = {
	"consectetur adipiscing elit, ",
	"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
}
local elem = a.div {
	"Lorem ipsum dolor sit amet, ",
	parts,
}
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>

函数

可以将函数作为子结点,这相当于调用函数,并将返回值作为子结点,唯一的区别在于函数将被推迟到tostring时调用。

local elem = a.ul {
	a.li "item 1",
	a.li {
		function()  -- function returning string
			return "item 2"
		end,
	}
	function()  -- function returning element
		return a.li "item 3"
	end,
	function()  -- function returning sequence
		local list = {}
		for i = 4, 6 do
			list[#list+1] = a.li("item "..i)
		end
		return list
	end,
}
print(elem)
<ul>
	<li>item 1</li>
	<li>item 2</li>
	<li>item 3</li>
	<li>item 4</li>
	<li>item 5</li>
	<li>item 6</li>
</ul>

方括号语法(设置元素属性)

在方括号内放置字符串可以快速设置idclass

local elem = a.div["#my-id my-class-1 my-class-2"] {
	a.p "You know what it is.",
}
print(elem)

在方括号内放置表可以设置元素属性,不局限于idclass。这让复用属性变得更方便。

local attr = {
	id="my-id",
	class="my-class-1 my-class-2",
}
local elem = a.div[attr] {
	a.p "You know what it is.",
}
print(elem)

上面两段代码的输出均为:

<div id="my-id" class="my-class-1 my-class-2">
	<p>You know what it is.</p>
</div>

斜杠语法(元素链)

local syntax = <elem1> / <elem2> / <elem3>
local example = a.main / a.div / a.p { ... }

相当于

local syntax = <elem1>(<elem2>(<elem3>))
local example = a.main {
	a.div {
		a.p { ... }
	}
}

前提是<elem1><elem2>不是空元素(如<br>)或已构建元素

local li_link = a.li / a.a
local elem = (
	a.header["site-header"] / a.nav / a.ul {
		li_link { href="/home", "Home" },
		li_link { href="/posts", "Posts" },
		li_link { href="/about", "About" },
	}
)
print(elem)
<header class="site-header">
	<nav>
		<ul>
			<li><a href="/home">Home</a></li>
			<li><a href="/posts">Posts</a></li>
			<li><a href="/about">About</a></li>
		</ul>
	</nav>
</header>

acandy.Fragment

Fragment 承载多个元素。a.Fragment和普通表的仅有的区别就是:

  • 设置了__tostring,可以得到HTML字符串;
  • 设置了__index,可以以类似面向对象的形式调用table.inserttable.removetable库中所有以表为第一个参数的方法。

可以通过a.Fragment()a.Fragment({})创建一个空的Fragment。

当仅有一个元素时,a.Fragment(<值>)a.Fragment({ <值> })等价。

例子:

local frag = a.Fragment {
	a.p "第一段。",
	a.p "第二段。",
}
frag:insert(a.p("第三段。"))
print(frag)
<p>第一段。</p>
<p>第二段。</p>
<p>第三段。</p>

acandy.Raw

a.Raw用于使字符串在最终不被转义。它接收任意类型的值,并调用tostring,存储于内部。

  • 设置了__tostring,可以得到对应字符串;
  • 设置了__concat,可以连接两个由a.Raw得到的对象。

例子:

local elem = a.ul {
	a.li "foo <br> bar",
	a.li(a.Raw "foo <br> bar"),
	a.li(a.Raw("foo <b")..a.Raw("r> bar")),
	a.li { a.Raw("foo <b"), a.Raw("r> bar") },
}
<ul>
	<li>foo &lt;br&gt; bar</li>
	<li>foo <br> bar</li>
	<li>foo <br> bar</li>
	<li>foo <br> bar</li>
</ul>

acandy.some

local frag1 = a.some.<tag>(<arg1>, <arg2>, ...)
local frag2 = a.some.<tag>[<attr>](<arg1>, <arg2>, ...)

相当于

local frag1 = a.Fragment {
	a.<tag>(<arg1>),
	a.<tag>(<arg2>),
	...,
}
local frag2 = a.Fragment {
	a.<tag>[<attr>](<arg1>),
	a.<tag>[<attr>](<arg2>),
	...,
}

例子:

local some = a.some
local items = a.ul(some.li['my-li']("item 1", "item 2"))
print(items)
<ul>
	<li class="my-li">item 1</li>
	<li class="my-li">item 2</li>
</ul>

元素实例属性

如果一个元素是a.div(...)a.div[...](...)这类进行函数调用得出的元素,则称它为“已构建元素”;已构建元素作为元素链末端的元素时,该元素链同样返回一个已构建元素;而a.diva.div[...]则不属于已构建元素。

对于一个已构建的元素elem,它有如下属性。

  • elem.tag_name:元素的标签名,可以重新赋值。
  • elem.attributes:一个表,存储着元素的所有属性,对此表的更改会生效于元素本身;不可重新赋值。
  • elem.children:一个Fragment,存储着元素的所有子结点,对此表的更改会生效于元素本身;不可重新赋值。
  • elem.some_attributesome_attribute为字符串):相当于 elem.attributes.some_attribute
  • elem[n]n为整数):相当于 elem.children[n]

例子:

local elem = a.ol { id="my-id",
   a.li "item 1",
}

-- get
elem.tag_name  --> "ol"

elem.children[1]  --> a.li "item 1"
elem[1] == elem.children[1]  --> true

elem.attributes.id  --> "my-id"
elem.id == elem.attributes.id  --> true

-- set
elem.tag_name = 'ul'

elem.children:insert(a.li "item 2")
elem[3] = a.li "item 3"

elem.attributes.id = "new-id"
elem.style = "color: blue;"

print(elem)
<ul id="new-id" style="color: blue;">
	<li>item 1</li>
	<li>item 2</li>
	<li>item 3</li>
</ul>