-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcomposeDataFromVersion2.ts
More file actions
127 lines (112 loc) · 3.62 KB
/
composeDataFromVersion2.ts
File metadata and controls
127 lines (112 loc) · 3.62 KB
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
import type { OutputData } from '@editorjs/editorjs';
import type { InlineFragment } from '@editorjs/model';
import { createInlineToolData, createInlineToolName, TextNode, ValueNode, type BlockNodeInit } from '@editorjs/model';
/**
* Removes HTML tags from the input string
* @param input - any string with HTML tags like '<b>bold</b> <a href="https://editorjs.io">link</a>'
*/
function stripTags(input: string): string {
return input.replace(/<\/?[^>]+(>|$)/g, '');
}
/**
* Extracts inline fragments from the HTML string
* @param html - any html string like '<b>bold</b> <a href="https://editorjs.io">link</a>'
*
* NOW ONLY <b>, <strong> AND <a> TAGS ARE SUPPORTED
* @todo support all inline tools
*/
function extractFragments(html: string): InlineFragment[] {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const fragments: InlineFragment[] = [];
let index = 0;
/**
* Traverses children of the parent node
* @param parent - node to traverse children from
* @param startIndex - start index of the text
*/
function traverseChildren(parent: HTMLElement, startIndex: number): number {
parent.childNodes.forEach((child) => {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
startIndex = traverse(child, startIndex);
});
return startIndex;
}
/**
* Traverses the node and its children
* @param node - node to traverse children from
* @param startIndex - start index of the text
*/
function traverse(node: ChildNode, startIndex: number): number {
if (node.nodeType === Node.TEXT_NODE) {
index += node.textContent?.length ?? 0;
return index;
}
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as HTMLElement;
const tagName = element.tagName.toLowerCase();
const currentStartIndex = startIndex;
if (tagName === 'b' || tagName === 'strong') {
index = traverseChildren(element, startIndex);
fragments.push({
tool: createInlineToolName('bold'),
range: [currentStartIndex, index],
});
} else if (tagName === 'a') {
const href = element.getAttribute('href') ?? '';
index = traverseChildren(element, startIndex);
fragments.push({
tool: createInlineToolName('link'),
data: createInlineToolData({
href,
}),
range: [currentStartIndex, index],
});
} else {
index = traverseChildren(element, startIndex);
}
}
return index;
}
traverseChildren(doc.body, 0);
return fragments;
}
/**
* Converst OutputData from version 2 to version 3
* @param data - OutputData from version 2
*/
export function composeDataFromVersion2(data: OutputData): {
/**
* The child BlockNodes of the EditorDocument
*/
blocks: BlockNodeInit[];
} {
return {
blocks: data.blocks.map((block) => {
return {
name: block.type,
data: Object.fromEntries(
Object
.entries(block.data as Record<string, unknown>)
.map(([key, value]) => {
if (typeof value === 'string') {
const fragments = extractFragments(value);
const textNode = new TextNode({
value: stripTags(value),
fragments,
});
return [
key, textNode.serialized,
];
} else {
const valueNode = new ValueNode({ value });
return [
key, valueNode.serialized,
];
}
})
),
};
}),
};
}