generated from obsidianmd/obsidian-sample-plugin
-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
utils.ts
137 lines (120 loc) · 3.61 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { TFile, TFolder } from "obsidian";
export class TreeNode {
// h2-1, h3-2, etc
key: string;
title: string;
level: number;
children: TreeNode[] = [];
parent: TreeNode;
constructor(key: string, title: string, level: number) {
this.key = key;
this.title = title;
this.level = level;
this.children = [];
}
}
/**
* h1 1
* h2 1.1
* h3 1.1.1
* h4 1.1.2.1
* h4 1.1.2.2
* h2 1.2
* h2 1.3
*/
export function getHeadingTree(doc = document) {
const headings = doc.querySelectorAll("h1, h2, h3, h4, h5, h6");
const root = new TreeNode("", "Root", 0);
let prev = root;
headings.forEach((heading: HTMLElement) => {
const level = parseInt(heading.tagName.slice(1));
const link = heading.querySelector("a.md-print-anchor") as HTMLLinkElement;
const regexMatch = /^af:\/\/(.+)$/.exec(link?.href ?? "");
if (!regexMatch) {
return;
}
const newNode = new TreeNode(regexMatch[1], heading.innerText, level);
while (prev.level >= level) {
prev = prev.parent;
}
// 保证 prev.level < level, 即 prev 是 curr 的父节点
prev.children.push(newNode);
newNode.parent = prev;
prev = newNode;
});
return root;
}
// modify heading/block, and get heading/block flag
export function modifyDest(doc: Document) {
const data = new Map();
doc.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((heading: HTMLElement, i) => {
const link = document.createElement("a") as HTMLAnchorElement;
const flag = `${heading.tagName.toLowerCase()}-${i}`;
link.href = `af://${flag}`;
link.className = "md-print-anchor";
heading.appendChild(link);
data.set(heading.dataset.heading, flag);
});
return data;
}
export function fixAnchors(doc: Document, dest: Map<string, string>, basename: string) {
doc.querySelectorAll("a.internal-link").forEach((el: HTMLAnchorElement, i) => {
const [title, anchor] = el.dataset.href?.split("#") ?? [];
if (anchor?.length > 0) {
if (title?.length > 0 && title != basename) {
return;
}
const flag = dest.get(anchor);
if (flag && !anchor.startsWith("^")) {
el.href = `an://${flag}`;
}
}
});
}
/**
* 等待函数,轮询检查条件是否满足,可设置超时时间。
* @param cond 条件函数,返回布尔值表示条件是否满足。
* @param timeout 超时时间(可选,默认为0,表示没有超时时间限制)。
* @returns 返回一个 Promise 对象,当条件满足时解决为 true,超时或发生错误时拒绝。
*/
export function waitFor(cond: (...args: unknown[]) => boolean, timeout = 0) {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const poll = () => {
if (cond()) {
resolve(true);
} else if (timeout > 0 && Date.now() - startTime >= timeout) {
reject(new Error("Timeout exceeded"));
} else {
setTimeout(poll, 500);
}
};
poll();
});
}
export const px2mm = (px: number) => {
return Math.round(px * 0.26458333333719);
};
export const mm2px = (mm: number) => {
return Math.round(mm * 3.779527559);
};
export function traverseFolder(path: TFolder | TFile): TFile[] {
if (path instanceof TFile) {
if (path.extension == "md") {
return [path];
} else {
return [];
}
}
const arr = [];
for (const item of path.children) {
arr.push(...traverseFolder(item as TFolder));
}
return arr;
}
// copy element attributes
export function copyAttributes(node: HTMLElement, attributes: NamedNodeMap) {
Array.from(attributes).forEach((attr) => {
node.setAttribute(attr.name, attr.value);
});
}