OLD | NEW |
1 /// A simple tree API that results from parsing html. Intended to be compatible | 1 /// A simple tree API that results from parsing html. Intended to be compatible |
2 /// with dart:html, but right now it resembles the classic JS DOM. | 2 /// with dart:html, but right now it resembles the classic JS DOM. |
3 library dom; | 3 library dom; |
4 | 4 |
5 import 'dart:collection'; | 5 import 'dart:collection'; |
6 import 'package:source_maps/span.dart' show FileSpan; | 6 import 'package:source_maps/span.dart' show FileSpan; |
7 | 7 |
8 import 'src/constants.dart'; | 8 import 'src/constants.dart'; |
9 import 'src/list_proxy.dart'; | 9 import 'src/list_proxy.dart'; |
10 import 'src/token.dart'; | 10 import 'src/token.dart'; |
(...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 | 644 |
645 Node _setParent(Node node) { | 645 Node _setParent(Node node) { |
646 // Note: we need to remove the node from its previous parent node, if any, | 646 // Note: we need to remove the node from its previous parent node, if any, |
647 // before updating its parent pointer to point at our parent. | 647 // before updating its parent pointer to point at our parent. |
648 node.remove(); | 648 node.remove(); |
649 node.parent = _parent; | 649 node.parent = _parent; |
650 return node; | 650 return node; |
651 } | 651 } |
652 | 652 |
653 void add(Node value) { | 653 void add(Node value) { |
654 super.add(_setParent(value)); | 654 if (value is DocumentFragment) { |
| 655 addAll(value.nodes); |
| 656 } else { |
| 657 super.add(_setParent(value)); |
| 658 } |
655 } | 659 } |
656 | 660 |
657 void addLast(Node value) => add(value); | 661 void addLast(Node value) => add(value); |
658 | 662 |
659 void addAll(Iterable<Node> collection) { | 663 void addAll(Iterable<Node> collection) { |
660 // Note: we need to be careful if collection is another NodeList. | 664 // Note: we need to be careful if collection is another NodeList. |
661 // In particular: | 665 // In particular: |
662 // 1. we need to copy the items before updating their parent pointers, | 666 // 1. we need to copy the items before updating their parent pointers, |
| 667 // _flattenDocFragments does a copy internally. |
663 // 2. we should update parent pointers in reverse order. That way they | 668 // 2. we should update parent pointers in reverse order. That way they |
664 // are removed from the original NodeList (if any) from the end, which | 669 // are removed from the original NodeList (if any) from the end, which |
665 // is faster. | 670 // is faster. |
666 var list = (collection is NodeList || collection is! List) | 671 var list = _flattenDocFragments(collection); |
667 ? collection.toList() : collection as List; | |
668 for (var node in list.reversed) _setParent(node); | 672 for (var node in list.reversed) _setParent(node); |
669 super.addAll(list); | 673 super.addAll(list); |
670 } | 674 } |
671 | 675 |
672 void insert(int index, Node value) { | 676 void insert(int index, Node value) { |
673 super.insert(index, _setParent(value)); | 677 if (value is DocumentFragment) { |
| 678 insertAll(index, value.nodes); |
| 679 } else { |
| 680 super.insert(index, _setParent(value)); |
| 681 } |
674 } | 682 } |
675 | 683 |
676 Node removeLast() => super.removeLast()..parent = null; | 684 Node removeLast() => super.removeLast()..parent = null; |
677 | 685 |
678 Node removeAt(int i) => super.removeAt(i)..parent = null; | 686 Node removeAt(int i) => super.removeAt(i)..parent = null; |
679 | 687 |
680 void clear() { | 688 void clear() { |
681 for (var node in this) node.parent = null; | 689 for (var node in this) node.parent = null; |
682 super.clear(); | 690 super.clear(); |
683 } | 691 } |
684 | 692 |
685 void operator []=(int index, Node value) { | 693 void operator []=(int index, Node value) { |
686 this[index].parent = null; | 694 if (value is DocumentFragment) { |
687 super[index] = _setParent(value); | 695 removeAt(index); |
| 696 insertAll(index, value.nodes); |
| 697 } else { |
| 698 this[index].parent = null; |
| 699 super[index] = _setParent(value); |
| 700 } |
688 } | 701 } |
689 | 702 |
690 // TODO(jmesserly): These aren't implemented in DOM _NodeListImpl, see | 703 // TODO(jmesserly): These aren't implemented in DOM _NodeListImpl, see |
691 // http://code.google.com/p/dart/issues/detail?id=5371 | 704 // http://code.google.com/p/dart/issues/detail?id=5371 |
692 void setRange(int start, int rangeLength, List<Node> from, | 705 void setRange(int start, int rangeLength, List<Node> from, |
693 [int startFrom = 0]) { | 706 [int startFrom = 0]) { |
694 if (from is NodeList) { | 707 if (from is NodeList) { |
695 // Note: this is presumed to make a copy | 708 // Note: this is presumed to make a copy |
696 from = from.sublist(startFrom, startFrom + rangeLength); | 709 from = from.sublist(startFrom, startFrom + rangeLength); |
697 } | 710 } |
698 // Note: see comment in [addAll]. We need to be careful about the order of | 711 // Note: see comment in [addAll]. We need to be careful about the order of |
699 // operations if [from] is also a NodeList. | 712 // operations if [from] is also a NodeList. |
700 for (int i = rangeLength - 1; i >= 0; i--) { | 713 for (int i = rangeLength - 1; i >= 0; i--) { |
701 this[start + i].parent = null; | 714 this[start + i] = from[startFrom + i]; |
702 super[start + i] = _setParent(from[startFrom + i]); | |
703 } | 715 } |
704 } | 716 } |
705 | 717 |
706 void replaceRange(int start, int end, Iterable<Node> newContents) { | 718 void replaceRange(int start, int end, Iterable<Node> newContents) { |
707 removeRange(start, end); | 719 removeRange(start, end); |
708 insertAll(start, newContents); | 720 insertAll(start, newContents); |
709 } | 721 } |
710 | 722 |
711 void removeRange(int start, int rangeLength) { | 723 void removeRange(int start, int rangeLength) { |
712 for (int i = start; i < rangeLength; i++) this[i].parent = null; | 724 for (int i = start; i < rangeLength; i++) this[i].parent = null; |
713 super.removeRange(start, rangeLength); | 725 super.removeRange(start, rangeLength); |
714 } | 726 } |
715 | 727 |
716 void removeWhere(bool test(Element e)) { | 728 void removeWhere(bool test(Element e)) { |
717 for (var node in where(test)) { | 729 for (var node in where(test)) { |
718 node.parent = null; | 730 node.parent = null; |
719 } | 731 } |
720 super.removeWhere(test); | 732 super.removeWhere(test); |
721 } | 733 } |
722 | 734 |
723 void retainWhere(bool test(Element e)) { | 735 void retainWhere(bool test(Element e)) { |
724 for (var node in where((n) => !test(n))) { | 736 for (var node in where((n) => !test(n))) { |
725 node.parent = null; | 737 node.parent = null; |
726 } | 738 } |
727 super.retainWhere(test); | 739 super.retainWhere(test); |
728 } | 740 } |
729 | 741 |
730 void insertAll(int index, List<Node> nodes) { | 742 void insertAll(int index, Iterable<Node> collection) { |
731 for (var node in nodes) _setParent(node); | 743 // Note: we need to be careful how we copy nodes. See note in addAll. |
732 super.insertAll(index, nodes); | 744 var list = _flattenDocFragments(collection); |
| 745 for (var node in list.reversed) _setParent(node); |
| 746 super.insertAll(index, list); |
| 747 } |
| 748 |
| 749 _flattenDocFragments(Iterable<Node> collection) { |
| 750 // Note: this function serves two purposes: |
| 751 // * it flattens document fragments |
| 752 // * it creates a copy of [collections] when `collection is NodeList`. |
| 753 var result = []; |
| 754 for (var node in collection) { |
| 755 if (node is DocumentFragment) { |
| 756 result.addAll(node.nodes); |
| 757 } else { |
| 758 result.add(node); |
| 759 } |
| 760 } |
| 761 return result; |
733 } | 762 } |
734 } | 763 } |
735 | 764 |
736 | 765 |
737 /// An indexable collection of a node's descendants in the document tree, | 766 /// An indexable collection of a node's descendants in the document tree, |
738 /// filtered so that only elements are in the collection. | 767 /// filtered so that only elements are in the collection. |
739 // TODO(jmesserly): this was copied from dart:html | 768 // TODO(jmesserly): this was copied from dart:html |
740 // TODO(jmesserly): "implements List<Element>" is a workaround for analyzer bug. | 769 // TODO(jmesserly): "implements List<Element>" is a workaround for analyzer bug. |
741 class FilteredElementList extends IterableBase<Element> with ListMixin<Element> | 770 class FilteredElementList extends IterableBase<Element> with ListMixin<Element> |
742 implements List<Element> { | 771 implements List<Element> { |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
927 | 956 |
928 class _ConcatTextVisitor extends TreeVisitor { | 957 class _ConcatTextVisitor extends TreeVisitor { |
929 final _str = new StringBuffer(); | 958 final _str = new StringBuffer(); |
930 | 959 |
931 String toString() => _str.toString(); | 960 String toString() => _str.toString(); |
932 | 961 |
933 visitText(Text node) { | 962 visitText(Text node) { |
934 _str.write(node.data); | 963 _str.write(node.data); |
935 } | 964 } |
936 } | 965 } |
OLD | NEW |