| OLD | NEW |
| 1 /** Internals to the tree builders. */ | 1 /** Internals to the tree builders. */ |
| 2 library treebuilder; | 2 library treebuilder; |
| 3 | 3 |
| 4 import 'dart:collection'; | 4 import 'dart:collection'; |
| 5 import 'package:html5lib/dom.dart'; | 5 import 'package:html5lib/dom.dart'; |
| 6 import 'package:source_maps/span.dart' show FileSpan; | 6 import 'package:source_maps/span.dart' show FileSpan; |
| 7 import 'constants.dart'; | 7 import 'constants.dart'; |
| 8 import 'list_proxy.dart'; | 8 import 'list_proxy.dart'; |
| 9 import 'token.dart'; | 9 import 'token.dart'; |
| 10 import 'utils.dart'; | 10 import 'utils.dart'; |
| 11 | 11 |
| 12 // The scope markers are inserted when entering object elements, | 12 // The scope markers are inserted when entering object elements, |
| 13 // marquees, table cells, and table captions, and are used to prevent formatting | 13 // marquees, table cells, and table captions, and are used to prevent formatting |
| 14 // from "leaking" into tables, object elements, and marquees. | 14 // from "leaking" into tables, object elements, and marquees. |
| 15 final Node Marker = null; | 15 const Node MARKER = null; |
| 16 | 16 |
| 17 // TODO(jmesserly): this should extend ListBase<Node>, but my simple attempt | 17 // TODO(jmesserly): this should extend ListBase<Node>, but my simple attempt |
| 18 // didn't work. | 18 // didn't work. |
| 19 class ActiveFormattingElements extends ListProxy<Node> { | 19 class ActiveFormattingElements extends ListProxy<Node> { |
| 20 ActiveFormattingElements() : super(); | 20 ActiveFormattingElements() : super(); |
| 21 | 21 |
| 22 // Override the "add" method. | 22 // Override the "add" method. |
| 23 // TODO(jmesserly): I'd rather not override this; can we do this in the | 23 // TODO(jmesserly): I'd rather not override this; can we do this in the |
| 24 // calling code instead? | 24 // calling code instead? |
| 25 void add(Node node) { | 25 void add(Node node) { |
| 26 int equalCount = 0; | 26 int equalCount = 0; |
| 27 if (node != Marker) { | 27 if (node != MARKER) { |
| 28 for (Node element in reversed) { | 28 for (Node element in reversed) { |
| 29 if (element == Marker) { | 29 if (element == MARKER) { |
| 30 break; | 30 break; |
| 31 } | 31 } |
| 32 if (_nodesEqual(element, node)) { | 32 if (_nodesEqual(element, node)) { |
| 33 equalCount += 1; | 33 equalCount += 1; |
| 34 } | 34 } |
| 35 if (equalCount == 3) { | 35 if (equalCount == 3) { |
| 36 remove(element); | 36 remove(element); |
| 37 break; | 37 break; |
| 38 } | 38 } |
| 39 } | 39 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 102 insertFromTable = false; | 102 insertFromTable = false; |
| 103 | 103 |
| 104 document = new Document(); | 104 document = new Document(); |
| 105 } | 105 } |
| 106 | 106 |
| 107 bool elementInScope(target, {String variant}) { | 107 bool elementInScope(target, {String variant}) { |
| 108 //If we pass a node in we match that. if we pass a string | 108 //If we pass a node in we match that. if we pass a string |
| 109 //match any node with that name | 109 //match any node with that name |
| 110 bool exactNode = target is Node && target.nameTuple != null; | 110 bool exactNode = target is Node && target.nameTuple != null; |
| 111 | 111 |
| 112 List listElements1 = scopingElements; | 112 List listElements1 = SCOPING_ELEMENTS; |
| 113 List listElements2 = const []; | 113 List listElements2 = const []; |
| 114 bool invert = false; | 114 bool invert = false; |
| 115 if (variant != null) { | 115 if (variant != null) { |
| 116 switch (variant) { | 116 switch (variant) { |
| 117 case "button": | 117 case "button": |
| 118 listElements2 = const [const Pair(Namespaces.html, "button")]; | 118 listElements2 = const [const Pair(Namespaces.html, "button")]; |
| 119 break; | 119 break; |
| 120 case "list": | 120 case "list": |
| 121 listElements2 = const [const Pair(Namespaces.html, "ol"), | 121 listElements2 = const [const Pair(Namespaces.html, "ol"), |
| 122 const Pair(Namespaces.html, "ul")]; | 122 const Pair(Namespaces.html, "ul")]; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 // code. It should still do the same though. | 154 // code. It should still do the same though. |
| 155 | 155 |
| 156 // Step 1: stop the algorithm when there's nothing to do. | 156 // Step 1: stop the algorithm when there's nothing to do. |
| 157 if (activeFormattingElements.length == 0) { | 157 if (activeFormattingElements.length == 0) { |
| 158 return; | 158 return; |
| 159 } | 159 } |
| 160 | 160 |
| 161 // Step 2 and step 3: we start with the last element. So i is -1. | 161 // Step 2 and step 3: we start with the last element. So i is -1. |
| 162 int i = activeFormattingElements.length - 1; | 162 int i = activeFormattingElements.length - 1; |
| 163 var entry = activeFormattingElements[i]; | 163 var entry = activeFormattingElements[i]; |
| 164 if (entry == Marker || openElements.contains(entry)) { | 164 if (entry == MARKER || openElements.contains(entry)) { |
| 165 return; | 165 return; |
| 166 } | 166 } |
| 167 | 167 |
| 168 // Step 6 | 168 // Step 6 |
| 169 while (entry != Marker && !openElements.contains(entry)) { | 169 while (entry != MARKER && !openElements.contains(entry)) { |
| 170 if (i == 0) { | 170 if (i == 0) { |
| 171 //This will be reset to 0 below | 171 //This will be reset to 0 below |
| 172 i = -1; | 172 i = -1; |
| 173 break; | 173 break; |
| 174 } | 174 } |
| 175 i -= 1; | 175 i -= 1; |
| 176 // Step 5: let entry be one earlier in the list. | 176 // Step 5: let entry be one earlier in the list. |
| 177 entry = activeFormattingElements[i]; | 177 entry = activeFormattingElements[i]; |
| 178 } | 178 } |
| 179 | 179 |
| (...skipping 19 matching lines...) Expand all Loading... |
| 199 | 199 |
| 200 // Step 11 | 200 // Step 11 |
| 201 if (element == activeFormattingElements.last) { | 201 if (element == activeFormattingElements.last) { |
| 202 break; | 202 break; |
| 203 } | 203 } |
| 204 } | 204 } |
| 205 } | 205 } |
| 206 | 206 |
| 207 void clearActiveFormattingElements() { | 207 void clearActiveFormattingElements() { |
| 208 var entry = activeFormattingElements.removeLast(); | 208 var entry = activeFormattingElements.removeLast(); |
| 209 while (activeFormattingElements.length > 0 && entry != Marker) { | 209 while (activeFormattingElements.length > 0 && entry != MARKER) { |
| 210 entry = activeFormattingElements.removeLast(); | 210 entry = activeFormattingElements.removeLast(); |
| 211 } | 211 } |
| 212 } | 212 } |
| 213 | 213 |
| 214 /** | 214 /** |
| 215 * Check if an element exists between the end of the active | 215 * Check if an element exists between the end of the active |
| 216 * formatting elements and the last marker. If it does, return it, else | 216 * formatting elements and the last marker. If it does, return it, else |
| 217 * return null. | 217 * return null. |
| 218 */ | 218 */ |
| 219 Node elementInActiveFormattingElements(String name) { | 219 Node elementInActiveFormattingElements(String name) { |
| 220 for (Node item in activeFormattingElements.reversed) { | 220 for (Node item in activeFormattingElements.reversed) { |
| 221 // Check for Marker first because if it's a Marker it doesn't have a | 221 // Check for Marker first because if it's a Marker it doesn't have a |
| 222 // name attribute. | 222 // name attribute. |
| 223 if (item == Marker) { | 223 if (item == MARKER) { |
| 224 break; | 224 break; |
| 225 } else if (item.tagName == name) { | 225 } else if (item.tagName == name) { |
| 226 return item; | 226 return item; |
| 227 } | 227 } |
| 228 } | 228 } |
| 229 return null; | 229 return null; |
| 230 } | 230 } |
| 231 | 231 |
| 232 void insertRoot(Token token) { | 232 void insertRoot(Token token) { |
| 233 var element = createElement(token); | 233 var element = createElement(token); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 272 ..attributes = token.data | 272 ..attributes = token.data |
| 273 ..sourceSpan = token.span; | 273 ..sourceSpan = token.span; |
| 274 openElements.last.nodes.add(element); | 274 openElements.last.nodes.add(element); |
| 275 openElements.add(element); | 275 openElements.add(element); |
| 276 return element; | 276 return element; |
| 277 } | 277 } |
| 278 | 278 |
| 279 Element insertElementTable(token) { | 279 Element insertElementTable(token) { |
| 280 /** Create an element and insert it into the tree */ | 280 /** Create an element and insert it into the tree */ |
| 281 var element = createElement(token); | 281 var element = createElement(token); |
| 282 if (!tableInsertModeElements.contains(openElements.last.tagName)) { | 282 if (!TABLE_INSERT_MODE_ELEMENTS.contains(openElements.last.tagName)) { |
| 283 return insertElementNormal(token); | 283 return insertElementNormal(token); |
| 284 } else { | 284 } else { |
| 285 // We should be in the InTable mode. This means we want to do | 285 // We should be in the InTable mode. This means we want to do |
| 286 // special magic element rearranging | 286 // special magic element rearranging |
| 287 var nodePos = getTableMisnestedNodePosition(); | 287 var nodePos = getTableMisnestedNodePosition(); |
| 288 if (nodePos[1] == null) { | 288 if (nodePos[1] == null) { |
| 289 // TODO(jmesserly): I don't think this is reachable. If insertFromTable | 289 // TODO(jmesserly): I don't think this is reachable. If insertFromTable |
| 290 // is true, there will be a <table> element open, and it always has a | 290 // is true, there will be a <table> element open, and it always has a |
| 291 // parent pointer. | 291 // parent pointer. |
| 292 nodePos[0].nodes.add(element); | 292 nodePos[0].nodes.add(element); |
| 293 } else { | 293 } else { |
| 294 nodePos[0].insertBefore(element, nodePos[1]); | 294 nodePos[0].insertBefore(element, nodePos[1]); |
| 295 } | 295 } |
| 296 openElements.add(element); | 296 openElements.add(element); |
| 297 } | 297 } |
| 298 return element; | 298 return element; |
| 299 } | 299 } |
| 300 | 300 |
| 301 /** Insert text data. */ | 301 /** Insert text data. */ |
| 302 void insertText(String data, FileSpan span) { | 302 void insertText(String data, FileSpan span) { |
| 303 var parent = openElements.last; | 303 var parent = openElements.last; |
| 304 | 304 |
| 305 if (!insertFromTable || insertFromTable && | 305 if (!insertFromTable || insertFromTable && |
| 306 !tableInsertModeElements.contains(openElements.last.tagName)) { | 306 !TABLE_INSERT_MODE_ELEMENTS.contains(openElements.last.tagName)) { |
| 307 _insertText(parent, data, span); | 307 _insertText(parent, data, span); |
| 308 } else { | 308 } else { |
| 309 // We should be in the InTable mode. This means we want to do | 309 // We should be in the InTable mode. This means we want to do |
| 310 // special magic element rearranging | 310 // special magic element rearranging |
| 311 var nodePos = getTableMisnestedNodePosition(); | 311 var nodePos = getTableMisnestedNodePosition(); |
| 312 _insertText(nodePos[0], data, span, nodePos[1]); | 312 _insertText(nodePos[0], data, span, nodePos[1]); |
| 313 } | 313 } |
| 314 } | 314 } |
| 315 | 315 |
| 316 /** | 316 /** |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 391 Document getDocument() => document; | 391 Document getDocument() => document; |
| 392 | 392 |
| 393 /** Return the final fragment. */ | 393 /** Return the final fragment. */ |
| 394 DocumentFragment getFragment() { | 394 DocumentFragment getFragment() { |
| 395 //XXX assert innerHTML | 395 //XXX assert innerHTML |
| 396 var fragment = new DocumentFragment(); | 396 var fragment = new DocumentFragment(); |
| 397 openElements[0].reparentChildren(fragment); | 397 openElements[0].reparentChildren(fragment); |
| 398 return fragment; | 398 return fragment; |
| 399 } | 399 } |
| 400 } | 400 } |
| OLD | NEW |