OLD | NEW |
1 /// This library contains extra APIs that aren't in the DOM, but are useful | 1 /// This library contains extra APIs that aren't in the DOM, but are useful |
2 /// when interacting with the parse tree. | 2 /// when interacting with the parse tree. |
3 library dom_parsing; | 3 library dom_parsing; |
4 | 4 |
5 import 'dom.dart'; | 5 import 'dom.dart'; |
| 6 import 'src/constants.dart' show rcdataElements; |
6 | 7 |
7 /// A simple tree visitor for the DOM nodes. | 8 /// A simple tree visitor for the DOM nodes. |
8 class TreeVisitor { | 9 class TreeVisitor { |
9 visit(Node node) { | 10 visit(Node node) { |
10 switch (node.nodeType) { | 11 switch (node.nodeType) { |
11 case Node.ELEMENT_NODE: return visitElement(node); | 12 case Node.ELEMENT_NODE: return visitElement(node); |
12 case Node.TEXT_NODE: return visitText(node); | 13 case Node.TEXT_NODE: return visitText(node); |
13 case Node.COMMENT_NODE: return visitComment(node); | 14 case Node.COMMENT_NODE: return visitComment(node); |
14 case Node.DOCUMENT_FRAGMENT_NODE: return visitDocumentFragment(node); | 15 case Node.DOCUMENT_FRAGMENT_NODE: return visitDocumentFragment(node); |
15 case Node.DOCUMENT_NODE: return visitDocument(node); | 16 case Node.DOCUMENT_NODE: return visitDocument(node); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 | 61 |
61 String toString() => _str.toString(); | 62 String toString() => _str.toString(); |
62 | 63 |
63 visitDocument(Document node) { | 64 visitDocument(Document node) { |
64 _str.write("<pre>"); | 65 _str.write("<pre>"); |
65 visitChildren(node); | 66 visitChildren(node); |
66 _str.write("</pre>"); | 67 _str.write("</pre>"); |
67 } | 68 } |
68 | 69 |
69 visitDocumentType(DocumentType node) { | 70 visitDocumentType(DocumentType node) { |
70 _str.write('<code class="markup doctype"><!DOCTYPE ${node.tagName}>' | 71 _str.write('<code class="markup doctype"><!DOCTYPE ${node.name}>' |
71 '</code>'); | 72 '</code>'); |
72 } | 73 } |
73 | 74 |
74 visitText(Text node) { | 75 visitText(Text node) { |
75 // TODO(jmesserly): would be nice to use _addOuterHtml directly. | 76 writeTextNodeAsHtml(_str, node); |
76 _str.write(node.outerHtml); | |
77 } | 77 } |
78 | 78 |
79 visitElement(Element node) { | 79 visitElement(Element node) { |
80 _str.write('<<code class="markup element-name">${node.tagName}</code>'); | 80 final tag = node.localName; |
| 81 _str.write('<<code class="markup element-name">$tag</code>'); |
81 if (node.attributes.length > 0) { | 82 if (node.attributes.length > 0) { |
82 node.attributes.forEach((key, v) { | 83 node.attributes.forEach((key, v) { |
83 v = htmlSerializeEscape(v, attributeMode: true); | 84 v = htmlSerializeEscape(v, attributeMode: true); |
84 _str.write(' <code class="markup attribute-name">$key</code>' | 85 _str.write(' <code class="markup attribute-name">$key</code>' |
85 '=<code class="markup attribute-value">"$v"</code>'); | 86 '=<code class="markup attribute-value">"$v"</code>'); |
86 }); | 87 }); |
87 } | 88 } |
88 if (node.nodes.length > 0) { | 89 if (node.nodes.length > 0) { |
89 _str.write(">"); | 90 _str.write(">"); |
90 visitChildren(node); | 91 visitChildren(node); |
91 } else if (isVoidElement(node.tagName)) { | 92 } else if (isVoidElement(tag)) { |
92 _str.write(">"); | 93 _str.write(">"); |
93 return; | 94 return; |
94 } | 95 } |
95 _str.write( | 96 _str.write( |
96 '</<code class="markup element-name">${node.tagName}</code>>'); | 97 '</<code class="markup element-name">$tag</code>>'); |
97 } | 98 } |
98 | 99 |
99 visitComment(Comment node) { | 100 visitComment(Comment node) { |
100 var data = htmlSerializeEscape(node.data); | 101 var data = htmlSerializeEscape(node.data); |
101 _str.write('<code class="markup comment"><!--${data}--></code>'); | 102 _str.write('<code class="markup comment"><!--${data}--></code>'); |
102 } | 103 } |
103 } | 104 } |
104 | 105 |
105 | 106 |
106 // TODO(jmesserly): reconcile this with dart:web htmlEscape. | 107 // TODO(jmesserly): reconcile this with dart:web htmlEscape. |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 bool isVoidElement(String tagName) { | 154 bool isVoidElement(String tagName) { |
154 switch (tagName) { | 155 switch (tagName) { |
155 case "area": case "base": case "br": case "col": case "command": | 156 case "area": case "base": case "br": case "col": case "command": |
156 case "embed": case "hr": case "img": case "input": case "keygen": | 157 case "embed": case "hr": case "img": case "input": case "keygen": |
157 case "link": case "meta": case "param": case "source": case "track": | 158 case "link": case "meta": case "param": case "source": case "track": |
158 case "wbr": | 159 case "wbr": |
159 return true; | 160 return true; |
160 } | 161 } |
161 return false; | 162 return false; |
162 } | 163 } |
| 164 |
| 165 /// Serialize text node according to: |
| 166 /// <http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#ht
ml-fragment-serialization-algorithm> |
| 167 void writeTextNodeAsHtml(StringBuffer str, Text node) { |
| 168 // Don't escape text for certain elements, notably <script>. |
| 169 final parent = node.parent; |
| 170 if (parent is Element) { |
| 171 var tag = parent.localName; |
| 172 if (rcdataElements.contains(tag) || tag == 'plaintext') { |
| 173 str.write(node.data); |
| 174 return; |
| 175 } |
| 176 } |
| 177 str.write(htmlSerializeEscape(node.data)); |
| 178 } |
OLD | NEW |