概述
之前写了Godot版本的ETD2Latex,也就是将Tab缩进格式的层级文本转化成Latex的cases环境嵌套,一直想写个JS版本的,之前基于AI转化代码修改,越改越乱,放弃了。
今天终于沉下心来,从零开始写了一个JS版本。也算把这个坑填了。
用处嘛,就是学习时写各种总结笔记,亲测有效,用来过了一场考试。
界面
实现基础功能,界面都是原生的HTML和JS,所以暂时有点丑。
目前版本:
- 可以使用空格作为缩进,可以是1个或多个空格,只要低一级的缩进数大于高一级的就行
- 实时转化为Latex公式的cases环境嵌套形式(Latex总结笔记)
- 使用KaTex渲染,直接在底部实时生成预览
代码
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS版ETD2Latex</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.9/katex.min.js"></script>
<script src="ETD2Latex.js"></script>
<style>
h1{
text-align: center;
margin-bottom: 10px;
}
.author{
text-align: center;
margin-bottom: 10px;
}
textarea{
width:calc(100% - 8px);
height: 200px;
}
.colums2{
display: grid;
gap: 10pt;
grid-template-columns: 1fr 1fr;
}
.col{
background-color: #ccc;
border:1px solid #444;
/* padding: 10pt; */
text-align: right;
}
.col h2{
margin: 0pt;
padding: 5px;
background-color: #444;
text-align: center;
color:white;
}
.col .col_body{
padding: 10pt;
}
button{
min-width: 100px;
min-height: 30px;
}
/* latex公式预览区域 */
#latex_div{
min-height: 100px;
background-color: #ccc;
padding: 10px;
}
span.info{
color:red;
margin-right: 5px;
}
</style>
</head>
<body>
<h1>Latex总结笔记生成器(网页版)</h1>
<div class="author">巽星石</div>
<!-- 分栏 -->
<div class="colums2">
<!-- 左侧列 -->
<div class="col">
<h2>ETD字符串</h2>
<div class="col_body">
<textarea id="etd_txt"></textarea><br>
<button id="cls_btn">清空</button>
</div>
</div>
<!-- 右侧列 -->
<div class="col">
<h2>Latex公式字符串</h2>
<div class="col_body">
<textarea id="latex_txt"></textarea><br>
<span class="info" id="info1"></span><button id="copy_btn">复制</button>
</div>
</div>
</div>
<h2>Latex预览</h2>
<div id="latex_div"></div>
</body>
<script>
// 解析ETD字符串
let etd_txt = document.getElementById("etd_txt")
let latex_txt = document.getElementById("latex_txt")
let latex_div = document.getElementById("latex_div")
let cls_btn = document.getElementById("cls_btn")
let copy_btn = document.getElementById("copy_btn")
let info_span = document.getElementById("info1")
// 清空
cls_btn.onclick = function(){
etd_txt.value = ""
etd_txt.onkeyup()
}
// 实时输入和生成
etd_txt.onkeyup = function(){
let tree = ETDTree.with_etd_str(etd_txt.value);
latex_txt.value = tree.to_latex();
katex.render(latex_txt.value,latex_div); // 渲染
}
// 复制
copy_btn.onclick = function(){
latex_txt.select();
navigator.clipboard.writeText(latex_txt.value);
info_span.innerText = "已完成拷贝";
}
</script>
</html>
ETD2Latex.js
// 节点
class ETDNode{
children = []
constructor(id,deep,text,p_node){
this.id = id
this.deep = deep
this.text = text
this.p_node = p_node
}
// 转化为字符串
to_string(is_last = false){
let sttr = ""
if(is_last){
sttr = "\t".repeat(this.deep) + this.text;
}else{
sttr = "\t".repeat(this.deep) + this.text + "\n";
}
let idx = 0;
if(this.children.length > 0){
sttr += "\n"
}
this.children.forEach(chd => {
sttr += chd.to_string(idx == this.children.length - 1);
idx += 1;
});
return sttr;
}
// 转换为Latex
to_latex(is_last = false){
let ltx = ""
if(this.children.length == 0){
if(is_last){
ltx = this.text + "\n";
}else{
ltx = this.text + "\\".repeat(2) + "\n";
}
}else{
ltx = this.text + "\n";
ltx += "\\begin{cases}\n"
let idx = 0;
this.children.forEach(chd => {
ltx += chd.to_latex(idx == this.children.length - 1);
idx += 1;
});
ltx += "\\end{cases}" + "\\".repeat(2) + "\n";
}
return ltx;
}
}
// 解析后的树
class ETDTree{
tree = null;
constructor(tree){
this.tree = tree;
}
// 以ETD字符串创建实例
static with_etd_str(etd_str){
let p_node = null // 父节点
let c_node = null // 当前节点
let line_idx = 0 // 行号
let tree = null // 树的根节点
for(let line of etd_str.split("\n")) {
let deep = line.length - line.trimStart().length // 当前深度
let text = line.trim() // 当前节点文本
if(p_node == null){
c_node = new ETDNode(line_idx,deep,text,p_node);
tree = c_node;
p_node = c_node;
}else{
if(p_node.deep < deep){
c_node = new ETDNode(line_idx,deep,text,p_node);
p_node.children.push(c_node);
p_node = c_node;
}else if(p_node.deep == deep){
p_node = p_node.p_node
c_node = new ETDNode(line_idx,deep,text,p_node);
p_node.children.push(c_node);
p_node = c_node;
}else{
p_node = p_node.p_node.p_node
c_node = new ETDNode(line_idx,deep,text,p_node);
p_node.children.push(c_node);
p_node = c_node;
}
}
line_idx += 1;
}
return new ETDTree(tree);
}
// 转化为字符串
to_string(){
return this.tree.to_string();
}
// 转换为Latex
to_latex(){
return this.tree.to_latex();
}
}