/// This library contains extra APIs that aren't in the DOM, but are useful /// when interacting with the parse tree. library; import 'dom.dart'; import 'html_escape.dart'; import 'src/constants.dart' show rcdataElements; // Export a function which was previously declared here. export 'html_escape.dart'; /// A simple tree visitor for the DOM nodes. class TreeVisitor { void visit(Node node) { return switch (node.nodeType) { Node.ELEMENT_NODE => visitElement(node as Element), Node.TEXT_NODE => visitText(node as Text), Node.COMMENT_NODE => visitComment(node as Comment), Node.DOCUMENT_FRAGMENT_NODE => visitDocumentFragment(node as DocumentFragment), Node.DOCUMENT_NODE => visitDocument(node as Document), Node.DOCUMENT_TYPE_NODE => visitDocumentType(node as DocumentType), _ => throw UnsupportedError('DOM node type ${node.nodeType}') }; } void visitChildren(Node node) { // Allow for mutations (remove works) while iterating. for (var child in node.nodes.toList(growable: false)) { visit(child); } } /// The fallback handler if the more specific visit method hasn't been /// overriden. Only use this from a subclass of [TreeVisitor], otherwise /// call [visit] instead. void visitNodeFallback(Node node) => visitChildren(node); void visitDocument(Document node) => visitNodeFallback(node); void visitDocumentType(DocumentType node) => visitNodeFallback(node); void visitText(Text node) => visitNodeFallback(node); // TODO(jmesserly): visit attributes. void visitElement(Element node) => visitNodeFallback(node); void visitComment(Comment node) => visitNodeFallback(node); void visitDocumentFragment(DocumentFragment node) => visitNodeFallback(node); } /// Converts the DOM tree into an HTML string with code markup suitable for /// displaying the HTML's source code with CSS colors for different parts of the /// markup. See also [CodeMarkupVisitor]. String htmlToCodeMarkup(Node node) { return (CodeMarkupVisitor()..visit(node)).toString(); } /// Converts the DOM tree into an HTML string with code markup suitable for /// displaying the HTML's source code with CSS colors for different parts of the /// markup. See also [htmlToCodeMarkup]. class CodeMarkupVisitor extends TreeVisitor { final StringBuffer _str; CodeMarkupVisitor() : _str = StringBuffer(); @override String toString() => _str.toString(); @override void visitDocument(Document node) { _str.write('
');
    visitChildren(node);
    _str.write('
'); } @override void visitDocumentType(DocumentType node) { _str.write('<!DOCTYPE ${node.name}>' ''); } @override void visitText(Text node) { writeTextNodeAsHtml(_str, node); } @override void visitElement(Element node) { final tag = node.localName; _str.write('<$tag'); if (node.attributes.isNotEmpty) { node.attributes.forEach((key, v) { v = htmlSerializeEscape(v, attributeMode: true); _str.write(' $key' '="$v"'); }); } if (node.nodes.isNotEmpty) { _str.write('>'); visitChildren(node); } else if (isVoidElement(tag)) { _str.write('>'); return; } _str.write('</$tag>'); } @override void visitComment(Comment node) { final data = htmlSerializeEscape(node.data!); _str.write('<!--$data-->'); } } /// Returns true if this tag name is a void element. /// This method is useful to a pretty printer, because void elements must not /// have an end tag. /// See also: . bool isVoidElement(String? tagName) { switch (tagName) { case 'area': case 'base': case 'br': case 'col': case 'command': case 'embed': case 'hr': case 'img': case 'input': case 'keygen': case 'link': case 'meta': case 'param': case 'source': case 'track': case 'wbr': return true; } return false; } /// Serialize text node according to: /// void writeTextNodeAsHtml(StringBuffer str, Text node) { // Don't escape text for certain elements, notably