Skip to content

Commit

Permalink
fix: convert html to document (AppFlowy-IO#152)
Browse files Browse the repository at this point in the history
* fix:html to node converter fixes

* html tests added

* html decoder optimization

* feat: refactor code

* mobile platforms added

---------

Co-authored-by: Lucas.Xu <[email protected]>
  • Loading branch information
alihassan143 and LucasXu0 authored Jun 1, 2023
1 parent c573b9a commit a77b25a
Show file tree
Hide file tree
Showing 4 changed files with 751 additions and 47 deletions.
112 changes: 66 additions & 46 deletions lib/src/plugins/html/html_document_decoder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
if (domNode is dom.Element) {
final localName = domNode.localName;
if (HTMLTags.formattingElements.contains(localName)) {
_parseFormattingElement(delta, domNode);
final attributes = _parserFormattingElementAttributes(domNode);
delta.insert(domNode.text, attributes: attributes);
} else if (HTMLTags.specialElements.contains(localName)) {
nodes.addAll(_parseSpecialElements(domNode));
nodes.addAll(
_parseSpecialElements(
domNode,
type: ParagraphBlockKeys.type,
),
);
}
} else if (domNode is dom.Text) {
delta.insert(domNode.text);
Expand All @@ -46,7 +52,10 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
return nodes;
}

Iterable<Node> _parseSpecialElements(dom.Element element) {
Iterable<Node> _parseSpecialElements(
dom.Element element, {
required String type,
}) {
final localName = element.localName;
switch (localName) {
case HTMLTags.h1:
Expand All @@ -60,22 +69,21 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
case HTMLTags.orderedList:
return _parseOrderListElement(element);
case HTMLTags.list:
return _parseListElement(element);
return _parseListElement(element, type: type);
case HTMLTags.paragraph:
return [_parseParagraphElement(element)];
case HTMLTags.blockQuote:
return [_parseBlockQuoteElement(element)];
case HTMLTags.image:
break;
return [_parseImageElement(element)];
default:
return [paragraphNode(text: element.text)];
}
return [];
}

void _parseFormattingElement(Delta delta, dom.Element element) {
Attributes _parserFormattingElementAttributes(dom.Element element) {
final localName = element.localName;
Attributes? attributes;
Attributes attributes = {};
switch (localName) {
case HTMLTags.bold || HTMLTags.strong:
attributes = {FlowyRichTextKeys.bold: true};
Expand All @@ -92,81 +100,93 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
case HTMLTags.code:
attributes = {FlowyRichTextKeys.code: true};
case HTMLTags.span:
attributes = _getDeltaAttributesFromHTMLAttributes(
element.attributes,
);
final deltaAttributes = _getDeltaAttributesFromHTMLAttributes(
element.attributes,
) ??
{};
attributes.addAll(deltaAttributes);
break;
case HTMLTags.anchor:
final href = element.attributes['href'];
if (href != null) {
attributes = {
FlowyRichTextKeys.href: href,
};
attributes = {FlowyRichTextKeys.href: href};
}
break;
default:
assert(false, 'Unknown formatting element: $element');
break;
}
delta.insert(element.text, attributes: attributes);
for (final child in element.children) {
attributes.addAll(_parserFormattingElementAttributes(child));
}
return attributes;
}

Node _parseHeadingElement(
dom.Element element, {
required int level,
}) =>
headingNode(
level: level,
delta: Delta()..insert(element.text),
);
}) {
final delta = _parseDeltaElement(element);
return headingNode(
level: level,
delta: delta,
);
}

Node _parseBlockQuoteElement(dom.Element element) => quoteNode(
delta: Delta()..insert(element.text),
);

Iterable<Node> _parseUnOrderListElement(dom.Element element) {
final children = element.nodes.toList().whereType<dom.Element>();
return children.map(
(e) => bulletedListNode(
delta: Delta()
..insert(
e.text,
),
),
);
final result = <Node>[];
for (final child in element.children) {
result.addAll(_parseListElement(child, type: NumberedListBlockKeys.type));
}
return result;
}

Iterable<Node> _parseOrderListElement(dom.Element element) {
final children = element.nodes.toList().whereType<dom.Element>();
return children.map(
(e) => numberedListNode(
delta: Delta()
..insert(
e.text,
),
),
);
final result = <Node>[];
for (final child in element.children) {
result.addAll(_parseListElement(child, type: NumberedListBlockKeys.type));
}
return result;
}

Iterable<Node> _parseListElement(dom.Element element) {
final children = element.nodes.toList().whereType<dom.Element>();
return children
.map((e) => _parseSpecialElements(e))
.expand((element) => element);
Iterable<Node> _parseListElement(
dom.Element element, {
required String type,
}) {
final delta = _parseDeltaElement(element);
return [
Node(type: type, attributes: {ParagraphBlockKeys.delta: delta.toJson()})
];
}

Node _parseParagraphElement(dom.Element element) {
// TODO: parse image and checkbox.
final delta = _parseDeltaElement(element);
return paragraphNode(delta: delta);
}

Node _parseImageElement(dom.Element element) {
final src = element.attributes['src'] ?? '';
return imageNode(
url: src,
);
}

Delta _parseDeltaElement(dom.Element element) {
final delta = Delta();
final children = element.nodes.toList();
for (final child in children) {
if (child is dom.Element) {
_parseFormattingElement(delta, child);
final attributes = _parserFormattingElementAttributes(child);
delta.insert(child.text, attributes: attributes);
} else {
delta.insert(child.text ?? '');
}
}
return paragraphNode(delta: delta);
return delta;
}

Attributes? _getDeltaAttributesFromHTMLAttributes(
Expand Down
4 changes: 3 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ version: 1.0.0-dev.0
homepage: https://github.com/AppFlowy-IO/appflowy-editor

platforms:
android:
ios:
linux:
macos:
windows:
web:
windows:

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
Loading

0 comments on commit a77b25a

Please sign in to comment.