-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathhtml_support.dart
119 lines (108 loc) · 3.61 KB
/
html_support.dart
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
import 'package:html/dom.dart' as h;
import 'package:markdown/markdown.dart' as m;
import 'package:flutter/material.dart';
import 'package:html/parser.dart';
import 'package:html/dom_parsing.dart';
import 'package:markdown_widget/markdown_widget.dart';
///see this issue: https://github.com/dart-lang/markdown/issues/284#event-3216258013
///use [htmlToMarkdown] to convert HTML in [m.Text] to [m.Node]
void htmlToMarkdown(h.Node? node, int deep, List<m.Node> mNodes) {
if (node == null) return;
if (node is h.Text) {
mNodes.add(m.Text(node.text));
} else if (node is h.Element) {
final tag = node.localName;
List<m.Node> children = [];
node.children.forEach((e) {
htmlToMarkdown(e, deep + 1, children);
});
m.Element element;
if (tag == MarkdownTag.img.name || tag == 'video') {
element = HtmlElement(tag!, children, node.text);
element.attributes.addAll(node.attributes.cast());
} else {
element = HtmlElement(tag!, children, node.text);
element.attributes.addAll(node.attributes.cast());
}
mNodes.add(element);
}
}
final RegExp tableRep =
RegExp(r'<table[^>]*>', multiLine: true, caseSensitive: true);
final RegExp htmlRep = RegExp(r'<[^>]*>', multiLine: true, caseSensitive: true);
///parse [m.Node] to [h.Node]
List<SpanNode> parseHtml(
m.Text node, {
ValueCallback<dynamic>? onError,
WidgetVisitor? visitor,
TextStyle? parentStyle,
}) {
try {
final text = node.textContent.replaceAll(
visitor?.splitRegExp ?? WidgetVisitor.defaultSplitRegExp, '');
if (!text.contains(htmlRep)) return [TextNode(text: node.text)];
h.DocumentFragment document = parseFragment(text);
return HtmlToSpanVisitor(visitor: visitor, parentStyle: parentStyle)
.toVisit(document.nodes.toList());
} catch (e) {
onError?.call(e);
return [TextNode(text: node.text)];
}
}
class HtmlElement extends m.Element {
final String textContent;
HtmlElement(String tag, List<m.Node>? children, this.textContent)
: super(tag, children);
}
class HtmlToSpanVisitor extends TreeVisitor {
final List<SpanNode> _spans = [];
final List<SpanNode> _spansStack = [];
final WidgetVisitor visitor;
final TextStyle parentStyle;
HtmlToSpanVisitor({WidgetVisitor? visitor, TextStyle? parentStyle})
: this.visitor = visitor ?? WidgetVisitor(),
this.parentStyle = parentStyle ?? TextStyle();
List<SpanNode> toVisit(List<h.Node> nodes) {
_spans.clear();
for (final node in nodes) {
final emptyNode = ConcreteElementNode(style: parentStyle);
_spans.add(emptyNode);
_spansStack.add(emptyNode);
visit(node);
_spansStack.removeLast();
}
final result = List.of(_spans);
_spans.clear();
_spansStack.clear();
return result;
}
@override
void visitText(h.Text node) {
final last = _spansStack.last;
if (last is ElementNode) {
final textNode = TextNode(text: node.text);
last.accept(textNode);
}
}
@override
void visitElement(h.Element node) {
final localName = node.localName ?? '';
final mdElement = m.Element(localName, []);
mdElement.attributes.addAll(node.attributes.cast());
SpanNode spanNode = visitor.getNodeByElement(mdElement, visitor.config);
if (spanNode is! ElementNode) {
final n = ConcreteElementNode(tag: localName, style: parentStyle);
n.accept(spanNode);
spanNode = n;
}
final last = _spansStack.last;
if (last is ElementNode) {
last.accept(spanNode);
}
_spansStack.add(spanNode);
for (var child in node.nodes.toList(growable: false)) {
visit(child);
}
_spansStack.removeLast();
}
}