@anyblock/any-block-core 3.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ABAlias.ts +298 -0
- package/ABConvertEvent.ts +307 -0
- package/ABConvertManager.ts +266 -0
- package/ABSetting.ts +114 -0
- package/LICENSE +661 -0
- package/README.md +162 -0
- package/converter/ABConvert.ts +158 -0
- package/converter/GeneratorDataTable.ts +40 -0
- package/converter/abc_c2list.ts +497 -0
- package/converter/abc_code.ts +93 -0
- package/converter/abc_deco.ts +793 -0
- package/converter/abc_dir_tree.ts +467 -0
- package/converter/abc_echarts.ts +539 -0
- package/converter/abc_ex.ts +112 -0
- package/converter/abc_list.ts +650 -0
- package/converter/abc_markmap.ts +119 -0
- package/converter/abc_mdit_container.ts +161 -0
- package/converter/abc_mermaid.ts +343 -0
- package/converter/abc_plantuml.ts +109 -0
- package/converter/abc_plantuml_tools.ts +236 -0
- package/converter/abc_table.ts +242 -0
- package/converter/abc_text.ts +237 -0
- package/demo.ts +236 -0
- package/index.min.ts +26 -0
- package/index.ts +26 -0
- package/locales/en.ts +88 -0
- package/locales/helper.ts +27 -0
- package/locales/zh-cn.ts +88 -0
- package/package.json +22 -0
- package/setting.ts +0 -0
- package/style/styles.css +1218 -0
- package/style/styles.min.css +1 -0
- package/style/styles.scss +1244 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 转换器_目录树
|
|
3
|
+
*
|
|
4
|
+
* md_str <-> md_str
|
|
5
|
+
* md_str <-> html
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ABCSetting, ABReg } from "../ABSetting"
|
|
9
|
+
import { ABConvert_IOEnum, ABConvert } from "./ABConvert"
|
|
10
|
+
import {ABConvertManager } from "../ABConvertManager"
|
|
11
|
+
import {ListProcess, type List_ListItem, type ListItem } from "./abc_list"
|
|
12
|
+
|
|
13
|
+
export interface LTableItem {
|
|
14
|
+
content: string, // 内容
|
|
15
|
+
level: number, // 行缩进,仅当首列为0时有效
|
|
16
|
+
tableRow: number, // 对应首行序列
|
|
17
|
+
tableColumn: number, // 对应首列序列,主要看是否第一列,如果是则是主体
|
|
18
|
+
tableRowSpan: 1, // 跨行数,一般总是一
|
|
19
|
+
type: string // 文件类型。仅第一列有效
|
|
20
|
+
}
|
|
21
|
+
export type List_LTableItem = LTableItem[]
|
|
22
|
+
|
|
23
|
+
export class DirProcess{
|
|
24
|
+
/** 列表转列表格 */
|
|
25
|
+
static list2lt(text: string, div: HTMLDivElement, modeT=false) {
|
|
26
|
+
let list_itemInfo = DirProcess.list2dtdata(text)
|
|
27
|
+
list_itemInfo = ListProcess.data2strict(list_itemInfo).map((item, index)=>{ return {
|
|
28
|
+
content: list_itemInfo[index].content,
|
|
29
|
+
level: item.level, // modi
|
|
30
|
+
tableRow: list_itemInfo[index].tableRow,
|
|
31
|
+
tableColumn: list_itemInfo[index].tableColumn,
|
|
32
|
+
tableRowSpan: list_itemInfo[index].tableRowSpan,
|
|
33
|
+
type: list_itemInfo[index].type,
|
|
34
|
+
}})
|
|
35
|
+
return DirProcess.dtdata2dt(list_itemInfo, div, modeT)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 列表转树形目录 */
|
|
39
|
+
static list2dt(text: string, div: HTMLDivElement, modeT=false) {
|
|
40
|
+
let list_itemInfo = DirProcess.list2dtdata(text)
|
|
41
|
+
list_itemInfo = ListProcess.data2strict(list_itemInfo).map((item, index)=>{ return {
|
|
42
|
+
content: list_itemInfo[index].content,
|
|
43
|
+
level: item.level, // modi
|
|
44
|
+
tableRow: list_itemInfo[index].tableRow,
|
|
45
|
+
tableColumn: list_itemInfo[index].tableColumn,
|
|
46
|
+
tableRowSpan: list_itemInfo[index].tableRowSpan,
|
|
47
|
+
type: list_itemInfo[index].type,
|
|
48
|
+
}})
|
|
49
|
+
return DirProcess.dtdata2dt(list_itemInfo, div, modeT, true)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 列表文本转列表表格数据
|
|
54
|
+
*
|
|
55
|
+
* 只能通过“|”符号实现跨列
|
|
56
|
+
* 所以这种是没有合并单元格
|
|
57
|
+
*
|
|
58
|
+
* 第一列的level总为0
|
|
59
|
+
*/
|
|
60
|
+
static list2dtdata(text: string): List_LTableItem{
|
|
61
|
+
// 表格行处理
|
|
62
|
+
const list_itemInfo:List_LTableItem = []
|
|
63
|
+
const list_text = text.split("\n")
|
|
64
|
+
let row_index = -1;
|
|
65
|
+
for (const line of list_text) { // 遍历文本行
|
|
66
|
+
const m_line = line.match(ABReg.reg_list_noprefix)
|
|
67
|
+
if (m_line) { // 新的表格行
|
|
68
|
+
row_index++;
|
|
69
|
+
const content = m_line[4] // 这一行的内容
|
|
70
|
+
const level_inline: number = m_line[1].length // 缩进数
|
|
71
|
+
list_itemInfo.push({
|
|
72
|
+
content: content.trimStart(),
|
|
73
|
+
level: level_inline,
|
|
74
|
+
tableRow: row_index,
|
|
75
|
+
tableColumn: 0,
|
|
76
|
+
type: "",
|
|
77
|
+
tableRowSpan: 1,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
else{ // 旧的表格行 (即内换行追加)
|
|
81
|
+
const itemInfo = list_itemInfo.pop()
|
|
82
|
+
if(itemInfo){
|
|
83
|
+
list_itemInfo.push({
|
|
84
|
+
content: itemInfo.content+"\n"+line.trim(), // modi
|
|
85
|
+
level: itemInfo.level,
|
|
86
|
+
tableRow: itemInfo.tableRow,
|
|
87
|
+
tableColumn: itemInfo.tableColumn,
|
|
88
|
+
type: itemInfo.type,
|
|
89
|
+
tableRowSpan: itemInfo.tableRowSpan,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 表格列处理
|
|
96
|
+
const list_itemInfo2:List_LTableItem = []
|
|
97
|
+
for (const row_item of list_itemInfo) { // 遍历表格行
|
|
98
|
+
const list_column_item: string[] = row_item.content.split(ABReg.inline_split)
|
|
99
|
+
for (let column_index=0; column_index<list_column_item.length; column_index++) { // 遍历表格列
|
|
100
|
+
// 第一列,需处理文件扩展名
|
|
101
|
+
let type = "";
|
|
102
|
+
if (column_index==0) {
|
|
103
|
+
if (list_column_item[column_index].trimEnd().endsWith("/")) {
|
|
104
|
+
type = "folder"
|
|
105
|
+
} else {
|
|
106
|
+
const parts = list_column_item[column_index].split('.');
|
|
107
|
+
if (parts.length === 0 || parts[parts.length - 1] === '') type = '';
|
|
108
|
+
else type = parts[parts.length - 1];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 填充
|
|
112
|
+
list_itemInfo2.push({
|
|
113
|
+
content: list_column_item[column_index].trimEnd(), // modi, 使用内联分割符时,尾空白应该总是无用的
|
|
114
|
+
level: row_item.level,
|
|
115
|
+
tableRow: row_index, // modi
|
|
116
|
+
tableColumn: column_index, // modi
|
|
117
|
+
type: type, // modi
|
|
118
|
+
tableRowSpan: row_item.tableRowSpan,
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return list_itemInfo2
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** 列表格数据转列表格
|
|
127
|
+
* 注意传入的列表数据应该符合:
|
|
128
|
+
* 第一列等级为0、没有分叉
|
|
129
|
+
*/
|
|
130
|
+
static dtdata2dt(
|
|
131
|
+
list_tableInfo: List_LTableItem,
|
|
132
|
+
div: HTMLDivElement,
|
|
133
|
+
modeT: boolean,
|
|
134
|
+
is_folder=false
|
|
135
|
+
){
|
|
136
|
+
// 避免根div直接包含两个dom
|
|
137
|
+
const div2 = document.createElement("div"); div.appendChild(div2); div2.classList.add("ab-list-table-parent")
|
|
138
|
+
|
|
139
|
+
// 列表格
|
|
140
|
+
let table: HTMLElement, thead: HTMLElement|undefined, tbody: HTMLElement;
|
|
141
|
+
{
|
|
142
|
+
// 表格数据 组装成表格
|
|
143
|
+
table = document.createElement("table"); div2.appendChild(table); table.classList.add("ab-table", "ab-list-table")
|
|
144
|
+
if (is_folder) table.classList.add("ab-table-folder")
|
|
145
|
+
if (modeT) table.setAttribute("modeT", "true")
|
|
146
|
+
|
|
147
|
+
// 创建表头和表体
|
|
148
|
+
{
|
|
149
|
+
if(list_tableInfo[0].content.indexOf("< ")==0){ // 判断是否有表头
|
|
150
|
+
thead = document.createElement("thead"); table.appendChild(thead);
|
|
151
|
+
list_tableInfo[0].content=list_tableInfo[0].content.replace(/^\<\s/,"")
|
|
152
|
+
}
|
|
153
|
+
tbody = document.createElement("tbody"); table.appendChild(tbody);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 创建表格内容
|
|
157
|
+
let tr: HTMLElement // 当前所在的表格行
|
|
158
|
+
let is_head: boolean = false // 当前位于表头吗,还是表体
|
|
159
|
+
let prev_tr: HTMLElement|null = null // 缓存上一行,用于判断是否可以折叠
|
|
160
|
+
for (let cell_index=0; cell_index<list_tableInfo.length; cell_index++) {
|
|
161
|
+
const cell_item = list_tableInfo[cell_index];
|
|
162
|
+
|
|
163
|
+
// 创建表格行
|
|
164
|
+
if (cell_item.tableColumn ==0) {
|
|
165
|
+
// 表头行(即是否第一行&&是否有表头)
|
|
166
|
+
if (cell_index==0 && thead){
|
|
167
|
+
is_head = true
|
|
168
|
+
tr = document.createElement("tr"); thead.appendChild(tr); // attr: {"tr_level": tr_line_level[index_line]}
|
|
169
|
+
}
|
|
170
|
+
// 非表头行
|
|
171
|
+
else{
|
|
172
|
+
is_head = false
|
|
173
|
+
tr = document.createElement("tr"); tbody.appendChild(tr);
|
|
174
|
+
// 表头有些属性是没有的
|
|
175
|
+
tr.classList.add("ab-foldable-tr");
|
|
176
|
+
tr.setAttribute("tr_level", cell_item.level.toString()); tr.setAttribute("is_fold", "false"); tr.setAttribute("able_fold", "false");
|
|
177
|
+
tr.setAttribute("type", cell_item.type);
|
|
178
|
+
}
|
|
179
|
+
// 处理表格行的可折叠属性。若该行缩进数更多,则上一项必然可被折叠。最后一个肯定不能折叠故不用尾判断
|
|
180
|
+
if (prev_tr
|
|
181
|
+
&& !isNaN(Number(prev_tr.getAttribute("tr_level")))
|
|
182
|
+
&& Number(prev_tr.getAttribute("tr_level")) < cell_item.level
|
|
183
|
+
){
|
|
184
|
+
prev_tr.setAttribute("able_fold", "true")
|
|
185
|
+
}
|
|
186
|
+
prev_tr = tr
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 创建表格单元格 (md版)
|
|
190
|
+
const td = document.createElement(is_head?"th":"td"); tr!.appendChild(td); td.setAttribute("rowspan", cell_item.tableRowSpan.toString());
|
|
191
|
+
if (cell_item.tableColumn==0 && is_folder) { // 首列,处理文件夹
|
|
192
|
+
const td_svg = document.createElement("div"); td.appendChild(td_svg); td_svg.classList.add("ab-list-table-svg")
|
|
193
|
+
// 文件夹/文件图标 // https://www.w3schools.com/css/css_icons.asp, https://fontawesome.com/
|
|
194
|
+
if (!is_head) {
|
|
195
|
+
if (cell_item.type=="folder") {
|
|
196
|
+
td_svg.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z"/></svg>`
|
|
197
|
+
cell_item.content = cell_item.content.trimEnd().slice(0, -1);
|
|
198
|
+
} else {
|
|
199
|
+
td_svg.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M320 464c8.8 0 16-7.2 16-16l0-288-80 0c-17.7 0-32-14.3-32-32l0-80L64 48c-8.8 0-16 7.2-16 16l0 384c0 8.8 7.2 16 16 16l256 0zM0 64C0 28.7 28.7 0 64 0L229.5 0c17 0 33.3 6.7 45.3 18.7l90.5 90.5c12 12 18.7 28.3 18.7 45.3L384 448c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 64z"/></svg>`
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const td_cell = document.createElement("div"); td.appendChild(td_cell); td_cell.classList.add("ab-list-table-witharrow");
|
|
204
|
+
if (cell_item.tableColumn==0 && is_folder) { // 首列,文件/文件夹部分不使用md渲染 (否则 `01. name` 这种文件名会被误渲染)
|
|
205
|
+
td_cell.innerHTML = cell_item.content
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
ABConvertManager.getInstance().m_renderMarkdownFn(cell_item.content, td_cell)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 折叠列表格 事件绑定
|
|
214
|
+
// TODO fix bug,这里的父子是伪的,无嵌套的。展开折叠有bug: 如level2折叠, level1折叠再展开, 不会保留level2的折叠状态
|
|
215
|
+
{
|
|
216
|
+
const l_tr:NodeListOf<HTMLElement> = tbody.querySelectorAll("tr")
|
|
217
|
+
for (let i=0; i<l_tr.length; i++){
|
|
218
|
+
const tr = l_tr[i]
|
|
219
|
+
const targetEl: HTMLElement = tr.querySelector(':scope>td:first-child') ?? tr // 优先使用第一列作为可点击区域
|
|
220
|
+
// 1. 二选一,正常绑定方法
|
|
221
|
+
// 当前ob使用
|
|
222
|
+
if (ABCSetting.env.startsWith("obsidian")) {
|
|
223
|
+
targetEl.onclick = ()=>{
|
|
224
|
+
const tr_level = Number(tr.getAttribute("tr_level"))
|
|
225
|
+
if (isNaN(tr_level)) return
|
|
226
|
+
const tr_isfold = tr.getAttribute("is_fold")
|
|
227
|
+
if (!tr_isfold) return
|
|
228
|
+
let flag_do_fold = false // 防止折叠最小层
|
|
229
|
+
for (let j=i+1; j<l_tr.length; j++){
|
|
230
|
+
const tr2 = l_tr[j]
|
|
231
|
+
const tr_level2 = Number(tr2.getAttribute("tr_level"))
|
|
232
|
+
if (isNaN(tr_level2)) break
|
|
233
|
+
if (tr_level2 <= tr_level) break // 影响所有后代级
|
|
234
|
+
// if (tr_level2 != tr_level+1) break // 影响下一级的 (话说这里可能有列表规范性问题?)
|
|
235
|
+
(tr_isfold == "true") ? tr2.style.display = "" : tr2.style.display = "none"
|
|
236
|
+
flag_do_fold = true
|
|
237
|
+
}
|
|
238
|
+
if (flag_do_fold) tr.setAttribute("is_fold", tr_isfold=="true"?"false":"true")
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// 2. 二选一,嵌入内联onclick
|
|
242
|
+
// 当前mdit (vuepress、app) 使用
|
|
243
|
+
else {
|
|
244
|
+
targetEl.setAttribute("onclick", `\
|
|
245
|
+
const tr = (this.tagName == "TD") ? this.parentNode : this
|
|
246
|
+
const l_tr = tr.parentNode.querySelectorAll("tr")
|
|
247
|
+
const i = ${i}
|
|
248
|
+
|
|
249
|
+
const tr_level = Number(tr.getAttribute("tr_level"))
|
|
250
|
+
if (isNaN(tr_level)) return
|
|
251
|
+
const tr_isfold = tr.getAttribute("is_fold")
|
|
252
|
+
if (!tr_isfold) return
|
|
253
|
+
let flag_do_fold = false // 防止折叠最小层
|
|
254
|
+
for (let j=i+1; j<l_tr.length; j++){
|
|
255
|
+
const tr2 = l_tr[j]
|
|
256
|
+
const tr_level2 = Number(tr2.getAttribute("tr_level"))
|
|
257
|
+
if (isNaN(tr_level2)) break
|
|
258
|
+
if (tr_level2 <= tr_level) break // 影响所有后代级
|
|
259
|
+
// if (tr_level2 != tr_level+1) break // 影响下一级的 (话说这里可能有列表规范性问题?)
|
|
260
|
+
(tr_isfold == "true") ? tr2.style.display = "" : tr2.style.display = "none"
|
|
261
|
+
flag_do_fold = true
|
|
262
|
+
}
|
|
263
|
+
if (flag_do_fold) tr.setAttribute("is_fold", tr_isfold=="true"?"false":"true")
|
|
264
|
+
`)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 折叠/展开所有项
|
|
270
|
+
{
|
|
271
|
+
// 折叠全列表格 事件绑定 // TODO,可以简化、复用。tr.onclick(这里加上可空传参)
|
|
272
|
+
const btn = document.createElement("button"); div2.appendChild(btn); btn.classList.add("ab-table-fold");
|
|
273
|
+
const svgStr_fold = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-fold-vertical-icon lucide-fold-vertical"><path d="M12 22v-6"/><path d="M12 8V2"/><path d="M4 12H2"/><path d="M10 12H8"/><path d="M16 12h-2"/><path d="M22 12h-2"/><path d="m15 19-3-3-3 3"/><path d="m15 5-3 3-3-3"/></svg>`
|
|
274
|
+
const svgStr_unfold = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-unfold-vertical-icon lucide-unfold-vertical"><path d="M12 22v-6"/><path d="M12 8V2"/><path d="M4 12H2"/><path d="M10 12H8"/><path d="M16 12h-2"/><path d="M22 12h-2"/><path d="m15 19-3 3-3-3"/><path d="m15 5-3-3-3 3"/></svg>`
|
|
275
|
+
btn.setAttribute("is_fold", "false"); btn.innerHTML = svgStr_fold;
|
|
276
|
+
// 1. 二选一,正常绑定方法
|
|
277
|
+
// 当前ob使用
|
|
278
|
+
if (ABCSetting.env.startsWith("obsidian")) {
|
|
279
|
+
btn.onclick = ()=>{
|
|
280
|
+
const is_all_fold = btn.getAttribute("is_fold")
|
|
281
|
+
if (is_all_fold=="true") {
|
|
282
|
+
btn.setAttribute("is_fold", "false"); btn.innerHTML = svgStr_fold;
|
|
283
|
+
const btn_subs = table.querySelectorAll("tr[is_fold='true']>td:first-child");
|
|
284
|
+
btn_subs.forEach((btn_sub) => { (btn_sub as HTMLElement).click() }) // 注意setAttr版本无断言
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
btn.setAttribute("is_fold", "true"); btn.innerHTML = svgStr_unfold;
|
|
288
|
+
const btn_subs = table.querySelectorAll("tr[is_fold='false']>td:first-child");
|
|
289
|
+
btn_subs.forEach((btn_sub) => { (btn_sub as HTMLElement).click() }) // 注意setAttr版本无断言
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// 2. 二选一,嵌入内联onclick
|
|
294
|
+
// 当前mdit (vuepress、app) 使用
|
|
295
|
+
else {
|
|
296
|
+
btn.setAttribute("onclick", `\
|
|
297
|
+
const btn = this;
|
|
298
|
+
const svgStr_fold = \`${svgStr_fold}\`;
|
|
299
|
+
const svgStr_unfold = \`${svgStr_unfold}\`;
|
|
300
|
+
const table = btn.parentNode?.querySelector("table");
|
|
301
|
+
if (!table) return;
|
|
302
|
+
|
|
303
|
+
const is_all_fold = btn.getAttribute("is_fold")
|
|
304
|
+
if (is_all_fold=="true") {
|
|
305
|
+
btn.setAttribute("is_fold", "false"); btn.innerHTML = svgStr_fold;
|
|
306
|
+
const btn_subs = table.querySelectorAll("tr[is_fold='true']>td:first-child");
|
|
307
|
+
btn_subs.forEach((btn_sub) => { btn_sub.click() }) // 注意setAttr版本无断言
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
btn.setAttribute("is_fold", "true"); btn.innerHTML = svgStr_unfold;
|
|
311
|
+
const btn_subs = table.querySelectorAll("tr[is_fold='false']>td:first-child");
|
|
312
|
+
btn_subs.forEach((btn_sub) => { btn_sub.click() }) // 注意setAttr版本无断言
|
|
313
|
+
}`
|
|
314
|
+
)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return div
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const _abc_list2lt = ABConvert.factory({
|
|
323
|
+
id: "list2lt",
|
|
324
|
+
name: "列表转列表表格",
|
|
325
|
+
match: /list2(md)?lt(T)?/,
|
|
326
|
+
default: "list2lt",
|
|
327
|
+
process_param: ABConvert_IOEnum.text,
|
|
328
|
+
process_return: ABConvert_IOEnum.el,
|
|
329
|
+
process: (el, header, content: string): HTMLElement=>{
|
|
330
|
+
const matchs = header.match(/list2(md)?lt(T)?/)
|
|
331
|
+
if (!matchs) return el
|
|
332
|
+
DirProcess.list2lt(content, el, matchs[2]=="T")
|
|
333
|
+
return el
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
const _abc_list2dt = ABConvert.factory({
|
|
338
|
+
id: "list2dt",
|
|
339
|
+
name: "列表转树状目录",
|
|
340
|
+
match: /list2(md)?dt(T)?/,
|
|
341
|
+
default: "list2dt",
|
|
342
|
+
process_param: ABConvert_IOEnum.text,
|
|
343
|
+
process_return: ABConvert_IOEnum.el,
|
|
344
|
+
process: (el, header, content: string): HTMLElement=>{
|
|
345
|
+
const matchs = header.match(/list2(md)?dt(T)?/)
|
|
346
|
+
if (!matchs) return el
|
|
347
|
+
DirProcess.list2dt(content, el, matchs[2]=="T")
|
|
348
|
+
return el
|
|
349
|
+
}
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
// =======================================================================
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* ascii目录数据
|
|
356
|
+
*
|
|
357
|
+
* @detail
|
|
358
|
+
* 用于指导制表符,前缀的所有可能性:
|
|
359
|
+
* 1. 根,无
|
|
360
|
+
* 2. 非最后的文件,("│ "/" ") * n + "├─ "
|
|
361
|
+
* 2. 最后一个文件,("│ "/" ") * n + "└─ "
|
|
362
|
+
*
|
|
363
|
+
* 一个最佳测试demo: (重点检查b21前面是否正确)
|
|
364
|
+
* .
|
|
365
|
+
* ├─ a/
|
|
366
|
+
* ├─ b/
|
|
367
|
+
* │ ├─ b1/
|
|
368
|
+
* │ └─ b2/
|
|
369
|
+
* │ └─ b21
|
|
370
|
+
* └─ c/
|
|
371
|
+
* └─ c1
|
|
372
|
+
*
|
|
373
|
+
* 参考项目:https://github.com/yzhong52/ascii_tree/,不过这个项目是用rust写的,最后也没怎么参考到
|
|
374
|
+
*/
|
|
375
|
+
export interface DirListItem extends ListItem {
|
|
376
|
+
type: string; // 类型 (folder/文件名后缀)
|
|
377
|
+
is_last: boolean; // 是否该文件夹下最后一个文件
|
|
378
|
+
pre_as_text: string; // ascii dir 会使用到这个前缀
|
|
379
|
+
}[]
|
|
380
|
+
export type List_DirListItem = DirListItem[]
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* listdata 转 dirdata
|
|
384
|
+
*
|
|
385
|
+
* TODO 未完成 with comment 功能
|
|
386
|
+
*/
|
|
387
|
+
function listdata2dirdata(list: List_ListItem): List_DirListItem {
|
|
388
|
+
// is_have_vbar[x]表示在该项的后面直到出现>x的level前,会出现x level
|
|
389
|
+
// 用于控制 "| " or " ",以及 "├─ " or "└─ "
|
|
390
|
+
// 空视为true
|
|
391
|
+
const is_have_vbar: boolean[] = [];
|
|
392
|
+
|
|
393
|
+
const newlist: List_DirListItem = [];
|
|
394
|
+
for (let i=0; i<list.length; i++) {
|
|
395
|
+
const item = list[i];
|
|
396
|
+
|
|
397
|
+
// 文件扩展名
|
|
398
|
+
let type: string;
|
|
399
|
+
if (item.content.trimEnd().endsWith("/")) {
|
|
400
|
+
type = "folder"
|
|
401
|
+
} else {
|
|
402
|
+
const parts = item.content.split('.');
|
|
403
|
+
if (parts.length === 0 || parts[parts.length - 1] === '') type = '';
|
|
404
|
+
else type = parts[parts.length - 1];
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// is_last。这里 O(n^2) 了,懒得优化
|
|
408
|
+
let is_last = true
|
|
409
|
+
for (let j=i+1; j<list.length; j++) {
|
|
410
|
+
if (list[j].level < item.level) {
|
|
411
|
+
is_last = true; break;
|
|
412
|
+
} else if (list[j].level == item.level) {
|
|
413
|
+
is_last = false; break;
|
|
414
|
+
} else {
|
|
415
|
+
continue
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
is_have_vbar[item.level] = !is_last // 例如 root 目录,is_last = true,应标记 is_have_vbar[0] = false
|
|
419
|
+
|
|
420
|
+
// 用 is_have_vbar 构造 pre_as_text
|
|
421
|
+
let pre_as_text = ""
|
|
422
|
+
if (item.level>1) {
|
|
423
|
+
for (let i = 1; i < item.level; i++) { // 从1开始,root层没有vbar
|
|
424
|
+
if (!is_have_vbar.hasOwnProperty(i)) pre_as_text += "[e]" // 一般不会出现这种情况,除非一层直接往下跳两层
|
|
425
|
+
else if (is_have_vbar[i]) pre_as_text += "| "
|
|
426
|
+
else pre_as_text += " "
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// 新list
|
|
431
|
+
newlist.push({
|
|
432
|
+
content: item.content,
|
|
433
|
+
level: item.level,
|
|
434
|
+
type: type,
|
|
435
|
+
is_last: is_last,
|
|
436
|
+
pre_as_text: pre_as_text
|
|
437
|
+
})
|
|
438
|
+
}
|
|
439
|
+
return newlist
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const _abc_list2astreeH = ABConvert.factory({
|
|
443
|
+
id: "list2astreeH",
|
|
444
|
+
name: "列表到sacii目录树",
|
|
445
|
+
process_param: ABConvert_IOEnum.text,
|
|
446
|
+
process_return: ABConvert_IOEnum.text,
|
|
447
|
+
process: (el, header, content: string): string=>{
|
|
448
|
+
let listdata: List_ListItem = ListProcess.list2data(content)
|
|
449
|
+
listdata = ListProcess.data2strict(listdata)
|
|
450
|
+
const dirlistdata: List_DirListItem = listdata2dirdata(listdata)
|
|
451
|
+
|
|
452
|
+
let newContent = ""
|
|
453
|
+
for (const item of dirlistdata) {
|
|
454
|
+
if (item.level == 0) {
|
|
455
|
+
newContent += item.content + "\n"
|
|
456
|
+
} else {
|
|
457
|
+
newContent +=
|
|
458
|
+
item.pre_as_text +
|
|
459
|
+
(item.is_last ? "└─ " : "├─ ") +
|
|
460
|
+
item.content + "\n"
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
newContent = newContent.trimEnd()
|
|
464
|
+
|
|
465
|
+
return newContent
|
|
466
|
+
}
|
|
467
|
+
})
|