@8btc/mditor 0.0.3 → 0.0.5

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.
@@ -1,27 +1,48 @@
1
- import {scrollCenter} from "../util/editorCommonEvent";
2
- import {hasClosestByAttribute} from "../util/hasClosest";
3
- import {getSelectPosition, setRangeByWbr} from "../util/selection";
4
- import {getSideByType, processAfterRender, processSpinVditorSVDOM} from "./process";
5
- import {combineFootnote} from "./combineFootnote";
1
+ import { scrollCenter } from "../util/editorCommonEvent";
2
+ import { hasClosestByAttribute } from "../util/hasClosest";
3
+ import { getSelectPosition, setRangeByWbr } from "../util/selection";
4
+ import {
5
+ getSideByType,
6
+ processAfterRender,
7
+ processSpinVditorSVDOM,
8
+ } from "./process";
9
+ import { combineFootnote } from "./combineFootnote";
6
10
 
7
11
  export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
8
12
  const range = getSelection().getRangeAt(0).cloneRange();
9
13
  let startContainer = range.startContainer;
10
- if (range.startContainer.nodeType !== 3 && (range.startContainer as HTMLElement).tagName === "DIV") {
14
+ if (
15
+ range.startContainer.nodeType !== 3 &&
16
+ (range.startContainer as HTMLElement).tagName === "DIV"
17
+ ) {
11
18
  startContainer = range.startContainer.childNodes[range.startOffset - 1];
12
19
  }
13
20
  let blockElement = hasClosestByAttribute(startContainer, "data-block", "0");
14
21
  // 不调用 lute 解析
15
- if (blockElement && event && (event.inputType === "deleteContentBackward" || event.data === " ")) {
22
+ if (
23
+ blockElement &&
24
+ event &&
25
+ (event.inputType === "deleteContentBackward" || event.data === " ")
26
+ ) {
16
27
  // 开始可以输入空格
17
- const startOffset = getSelectPosition(blockElement, vditor.sv.element, range).start;
28
+ const startOffset = getSelectPosition(
29
+ blockElement,
30
+ vditor.sv.element,
31
+ range
32
+ ).start;
18
33
  let startSpace = true;
19
- for (let i = startOffset - 1;
34
+ for (
35
+ let i = startOffset - 1;
20
36
  // 软换行后有空格
21
- i > blockElement.textContent.substr(0, startOffset).lastIndexOf("\n"); i--) {
22
- if (blockElement.textContent.charAt(i) !== " " &&
37
+ i >
38
+ blockElement.textContent.substr(0, startOffset).lastIndexOf("\n");
39
+ i--
40
+ ) {
41
+ if (
42
+ blockElement.textContent.charAt(i) !== " " &&
23
43
  // 多个 tab 前删除不形成代码块 https://github.com/Vanessa219/vditor/issues/162 1
24
- blockElement.textContent.charAt(i) !== "\t") {
44
+ blockElement.textContent.charAt(i) !== "\t"
45
+ ) {
25
46
  startSpace = false;
26
47
  break;
27
48
  }
@@ -37,63 +58,120 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
37
58
  if (event.inputType === "deleteContentBackward") {
38
59
  // https://github.com/Vanessa219/vditor/issues/584 代码块 marker 删除
39
60
  const codeBlockMarkerElement =
40
- hasClosestByAttribute(startContainer, "data-type", "code-block-open-marker") ||
41
- hasClosestByAttribute(startContainer, "data-type", "code-block-close-marker");
61
+ hasClosestByAttribute(
62
+ startContainer,
63
+ "data-type",
64
+ "code-block-open-marker"
65
+ ) ||
66
+ hasClosestByAttribute(
67
+ startContainer,
68
+ "data-type",
69
+ "code-block-close-marker"
70
+ );
42
71
  if (codeBlockMarkerElement) {
43
- if (codeBlockMarkerElement.getAttribute("data-type") === "code-block-close-marker") {
44
- const openMarkerElement = getSideByType(startContainer, "code-block-open-marker");
72
+ if (
73
+ codeBlockMarkerElement.getAttribute("data-type") ===
74
+ "code-block-close-marker"
75
+ ) {
76
+ const openMarkerElement = getSideByType(
77
+ startContainer,
78
+ "code-block-open-marker"
79
+ );
45
80
  if (openMarkerElement) {
46
- openMarkerElement.textContent = codeBlockMarkerElement.textContent;
81
+ openMarkerElement.textContent =
82
+ codeBlockMarkerElement.textContent;
47
83
  processAfterRender(vditor);
48
84
  return;
49
85
  }
50
86
  }
51
- if (codeBlockMarkerElement.getAttribute("data-type") === "code-block-open-marker") {
52
- const openMarkerElement = getSideByType(startContainer, "code-block-close-marker", false);
87
+ if (
88
+ codeBlockMarkerElement.getAttribute("data-type") ===
89
+ "code-block-open-marker"
90
+ ) {
91
+ const openMarkerElement = getSideByType(
92
+ startContainer,
93
+ "code-block-close-marker",
94
+ false
95
+ );
53
96
  if (openMarkerElement) {
54
- openMarkerElement.textContent = codeBlockMarkerElement.textContent;
97
+ openMarkerElement.textContent =
98
+ codeBlockMarkerElement.textContent;
55
99
  processAfterRender(vditor);
56
100
  return;
57
101
  }
58
102
  }
59
103
  }
60
104
  // https://github.com/Vanessa219/vditor/issues/877 数学公式输入删除生成节点
61
- const mathBlockMarkerElement =
62
- hasClosestByAttribute(startContainer, "data-type", "math-block-open-marker");
105
+ const mathBlockMarkerElement = hasClosestByAttribute(
106
+ startContainer,
107
+ "data-type",
108
+ "math-block-open-marker"
109
+ );
63
110
  if (mathBlockMarkerElement) {
64
- const mathBlockCloseElement = mathBlockMarkerElement.nextElementSibling.nextElementSibling;
65
- if (mathBlockCloseElement && mathBlockCloseElement.getAttribute("data-type") === "math-block-close-marker") {
111
+ const mathBlockCloseElement =
112
+ mathBlockMarkerElement.nextElementSibling
113
+ .nextElementSibling;
114
+ if (
115
+ mathBlockCloseElement &&
116
+ mathBlockCloseElement.getAttribute("data-type") ===
117
+ "math-block-close-marker"
118
+ ) {
66
119
  mathBlockCloseElement.remove();
67
120
  processAfterRender(vditor);
68
121
  }
69
122
  return;
70
123
  }
71
124
 
72
- blockElement.querySelectorAll('[data-type="code-block-open-marker"]').forEach((item: HTMLElement) => {
73
- if (item.textContent.length === 1) {
74
- item.remove();
75
- }
76
- });
77
- blockElement.querySelectorAll('[data-type="code-block-close-marker"]').forEach((item: HTMLElement) => {
78
- if (item.textContent.length === 1) {
79
- item.remove();
80
- }
81
- });
125
+ blockElement
126
+ .querySelectorAll('[data-type="code-block-open-marker"]')
127
+ .forEach((item: HTMLElement) => {
128
+ if (item.textContent.length === 1) {
129
+ item.remove();
130
+ }
131
+ });
132
+ blockElement
133
+ .querySelectorAll('[data-type="code-block-close-marker"]')
134
+ .forEach((item: HTMLElement) => {
135
+ if (item.textContent.length === 1) {
136
+ item.remove();
137
+ }
138
+ });
82
139
 
83
140
  // 标题删除
84
- const headingElement = hasClosestByAttribute(startContainer, "data-type", "heading-marker");
85
- if (headingElement && headingElement.textContent.indexOf("#") === -1) {
141
+ const headingElement = hasClosestByAttribute(
142
+ startContainer,
143
+ "data-type",
144
+ "heading-marker"
145
+ );
146
+ if (
147
+ headingElement &&
148
+ headingElement.textContent.indexOf("#") === -1
149
+ ) {
86
150
  processAfterRender(vditor);
87
151
  return;
88
152
  }
89
153
  }
90
154
  // 删除或空格不解析,否则会 format 回去
91
- if ((event.data === " " || event.inputType === "deleteContentBackward") &&
92
- (hasClosestByAttribute(startContainer, "data-type", "padding") // 场景:b 前进行删除 [> 1. a\n> b]
93
- || hasClosestByAttribute(startContainer, "data-type", "li-marker") // 场景:删除最后一个字符 [* 1\n* ]
94
- || hasClosestByAttribute(startContainer, "data-type", "task-marker") // 场景:删除最后一个字符 [* [ ] ]
95
- || hasClosestByAttribute(startContainer, "data-type", "blockquote-marker") // 场景:删除最后一个字符 [> ]
96
- )) {
155
+ if (
156
+ (event.data === " " ||
157
+ event.inputType === "deleteContentBackward") &&
158
+ (hasClosestByAttribute(startContainer, "data-type", "padding") || // 场景:b 前进行删除 [> 1. a\n> b]
159
+ hasClosestByAttribute(
160
+ startContainer,
161
+ "data-type",
162
+ "li-marker"
163
+ ) || // 场景:删除最后一个字符 [* 1\n* ]
164
+ hasClosestByAttribute(
165
+ startContainer,
166
+ "data-type",
167
+ "task-marker"
168
+ ) || // 场景:删除最后一个字符 [* [ ] ]
169
+ hasClosestByAttribute(
170
+ startContainer,
171
+ "data-type",
172
+ "blockquote-marker"
173
+ )) // 场景:删除最后一个字符 [> ]
174
+ ) {
97
175
  processAfterRender(vditor);
98
176
  return;
99
177
  }
@@ -106,7 +184,10 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
106
184
  if (!blockElement) {
107
185
  blockElement = vditor.sv.element;
108
186
  }
109
- if (blockElement.firstElementChild?.getAttribute("data-type") === "link-ref-defs-block") {
187
+ if (
188
+ blockElement.firstElementChild?.getAttribute("data-type") ===
189
+ "link-ref-defs-block"
190
+ ) {
110
191
  // 修改链接引用
111
192
  blockElement = vditor.sv.element;
112
193
  }
@@ -120,10 +201,12 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
120
201
  range.insertNode(document.createTextNode(Lute.Caret));
121
202
  }
122
203
  // 清除浏览器自带的样式
123
- blockElement.querySelectorAll("[style]").forEach((item) => { // 不可前置,否则会影响 newline 的样式
204
+ blockElement.querySelectorAll("[style]").forEach((item) => {
205
+ // 不可前置,否则会影响 newline 的样式
124
206
  item.removeAttribute("style");
125
207
  });
126
- blockElement.querySelectorAll("font").forEach((item) => { // 不可前置,否则会影响光标的位置
208
+ blockElement.querySelectorAll("font").forEach((item) => {
209
+ // 不可前置,否则会影响光标的位置
127
210
  item.outerHTML = item.innerHTML;
128
211
  });
129
212
  let html = blockElement.textContent;
@@ -136,28 +219,45 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
136
219
  html = blockElement.previousElementSibling.textContent + html;
137
220
  blockElement.previousElementSibling.remove();
138
221
  }
139
- if (blockElement.previousElementSibling && html.indexOf("---\n") === 0) {
222
+ if (
223
+ blockElement.previousElementSibling &&
224
+ html.indexOf("---\n") === 0
225
+ ) {
140
226
  // 确认 yaml-front 是否为首行
141
227
  html = blockElement.previousElementSibling.textContent + html;
142
228
  blockElement.previousElementSibling.remove();
143
229
  }
144
230
  // 添加链接引用
145
- let footnotes = ""
231
+ let footnotes = "";
146
232
 
147
- vditor.sv.element.querySelectorAll("[data-type='link-ref-defs-block']").forEach((item, index) => {
148
- if (item && !(blockElement as HTMLElement).isEqualNode(item.parentElement)) {
149
- footnotes += item.parentElement.textContent + "\n";
150
- item.parentElement.remove();
151
- }
152
- });
233
+ vditor.sv.element
234
+ .querySelectorAll("[data-type='link-ref-defs-block']")
235
+ .forEach((item, index) => {
236
+ if (
237
+ item &&
238
+ !(blockElement as HTMLElement).isEqualNode(
239
+ item.parentElement
240
+ )
241
+ ) {
242
+ footnotes += item.parentElement.textContent + "\n";
243
+ item.parentElement.remove();
244
+ }
245
+ });
153
246
 
154
247
  // 添加脚注到文章头,便于lute处理
155
- vditor.sv.element.querySelectorAll("[data-type='footnotes-link']").forEach((item, index) => {
156
- if (item && !(blockElement as HTMLElement).isEqualNode(item.parentElement)) {
157
- footnotes += item.parentElement.textContent + "\n";
158
- item.parentElement.remove();
159
- }
160
- });
248
+ vditor.sv.element
249
+ .querySelectorAll("[data-type='footnotes-link']")
250
+ .forEach((item, index) => {
251
+ if (
252
+ item &&
253
+ !(blockElement as HTMLElement).isEqualNode(
254
+ item.parentElement
255
+ )
256
+ ) {
257
+ footnotes += item.parentElement.textContent + "\n";
258
+ item.parentElement.remove();
259
+ }
260
+ });
161
261
  html = footnotes + html;
162
262
  }
163
263
  html = processSpinVditorSVDOM(html, vditor);
@@ -167,14 +267,19 @@ export const inputEvent = (vditor: IVditor, event?: InputEvent) => {
167
267
  blockElement.outerHTML = html;
168
268
  }
169
269
 
170
- vditor.sv.element.querySelectorAll("[data-type='link-ref-defs-block']").forEach((item) => {
171
- vditor.sv.element.insertAdjacentElement("beforeend", item.parentElement)
172
- })
270
+ vditor.sv.element
271
+ .querySelectorAll("[data-type='link-ref-defs-block']")
272
+ .forEach((item) => {
273
+ vditor.sv.element.insertAdjacentElement(
274
+ "beforeend",
275
+ item.parentElement
276
+ );
277
+ });
173
278
 
174
279
  // 合并脚注
175
280
  combineFootnote(vditor.sv.element, (root: HTMLElement) => {
176
- vditor.sv.element.insertAdjacentElement("beforeend", root)
177
- })
281
+ vditor.sv.element.insertAdjacentElement("beforeend", root);
282
+ });
178
283
 
179
284
  setRangeByWbr(vditor.sv.element, range);
180
285
 
@@ -7,7 +7,7 @@ import { log } from "../util/log";
7
7
  import { getEditorRange, setRangeByWbr } from "../util/selection";
8
8
  import { inputEvent } from "./inputEvent";
9
9
  import { combineFootnote } from "./combineFootnote";
10
- import { attachLineNumbersToBlocks } from "../util/attachLineNumbers";
10
+ import { attachLineNumbersToBlocksThrottled } from "../util/attachLineNumbers";
11
11
 
12
12
  export const processPaste = (vditor: IVditor, text: string) => {
13
13
  const range = getEditorRange(vditor);
@@ -178,7 +178,9 @@ export const processAfterRender = (
178
178
  vditor.undo.addToUndoStack(vditor);
179
179
  }
180
180
  try {
181
- attachLineNumbersToBlocks(vditor.sv.element, text);
181
+ if (vditor.options.lineNumber) {
182
+ attachLineNumbersToBlocksThrottled(vditor.sv.element, text);
183
+ }
182
184
  } catch {
183
185
  void 0;
184
186
  }
@@ -152,9 +152,10 @@ export const attachLineNumbersToBlocks = (
152
152
  const usedOrderedLines = new Set<number>();
153
153
  const ORDERED_RE =
154
154
  /\s*\d+(?:[.)、.。]|[\uFF0E\uFF09\u3001])\s+(?:\[(?: |x|X)\]\s+)?(.*)/;
155
+ const ORDERED_FULL = new RegExp(`^${ORDERED_RE.source}$`);
155
156
  for (let i = 0; i < srcLines.length; i++) {
156
157
  const raw = srcLines[i];
157
- const m = raw.match(new RegExp(`^${ORDERED_RE.source}$`));
158
+ const m = raw.match(ORDERED_FULL);
158
159
  if (m && typeof m[1] === "string") {
159
160
  const contentStripped = stripInlineForList(m[1]);
160
161
  orderedListEntries.push({ ln: i + 1, content: contentStripped });
@@ -172,12 +173,9 @@ export const attachLineNumbersToBlocks = (
172
173
  const orderedGroups: Array<number[]> = [];
173
174
  for (let i = 0; i < srcLines.length; ) {
174
175
  const raw = srcLines[i];
175
- if (new RegExp(`^${ORDERED_RE.source}$`).test(raw)) {
176
+ if (ORDERED_FULL.test(raw)) {
176
177
  const group: number[] = [];
177
- while (
178
- i < srcLines.length &&
179
- new RegExp(`^${ORDERED_RE.source}$`).test(srcLines[i])
180
- ) {
178
+ while (i < srcLines.length && ORDERED_FULL.test(srcLines[i])) {
181
179
  group.push(i + 1);
182
180
  i++;
183
181
  }
@@ -435,3 +433,31 @@ export const attachLineNumbersToBlocks = (
435
433
  }
436
434
  });
437
435
  };
436
+
437
+ /**
438
+ * 节流后的行号更新函数,避免频繁更新 data-linenumber
439
+ */
440
+ export const attachLineNumbersToBlocksThrottled = (() => {
441
+ let last = 0;
442
+ let timer = 0;
443
+ let lastArgs: [HTMLElement, string] | null = null;
444
+ const wait = 500;
445
+ const invoke = () => {
446
+ timer = 0;
447
+ last = Date.now();
448
+ const args = lastArgs;
449
+ lastArgs = null;
450
+ if (args) {
451
+ attachLineNumbersToBlocks(args[0], args[1]);
452
+ }
453
+ };
454
+ return (root: HTMLElement, source: string) => {
455
+ const now = Date.now();
456
+ lastArgs = [root, source];
457
+ if (now - last >= wait) {
458
+ invoke();
459
+ } else if (!timer) {
460
+ timer = window.setTimeout(invoke, wait - (now - last));
461
+ }
462
+ };
463
+ })();
@@ -1,6 +1,6 @@
1
1
  import { getMarkdown } from "../markdown/getMarkdown";
2
2
  import { accessLocalStorage } from "../util/compatibility";
3
- import { attachLineNumbersToBlocks } from "../util/attachLineNumbers";
3
+ import { attachLineNumbersToBlocksThrottled } from "../util/attachLineNumbers";
4
4
 
5
5
  export const afterRenderEvent = (
6
6
  vditor: IVditor,
@@ -43,7 +43,12 @@ export const afterRenderEvent = (
43
43
  }
44
44
 
45
45
  try {
46
- attachLineNumbersToBlocks(vditor.wysiwyg.element, text);
46
+ if (vditor.options.lineNumber) {
47
+ attachLineNumbersToBlocksThrottled(
48
+ vditor.wysiwyg.element,
49
+ text
50
+ );
51
+ }
47
52
  } catch {
48
53
  void 0;
49
54
  }