| // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| part of html_common; |
| |
| /** |
| * An indexable collection of a node's direct descendants in the document tree, |
| * filtered so that only elements are in the collection. |
| */ |
| class FilteredElementList extends ListBase<Element> implements NodeListWrapper { |
| final Node _node; |
| final List<Node> _childNodes; |
| |
| /** |
| * Creates a collection of the elements that descend from a node. |
| * |
| * Example usage: |
| * |
| * var filteredElements = new FilteredElementList(query("#container")); |
| * // filteredElements is [a, b, c]. |
| */ |
| FilteredElementList(Node node) |
| : _childNodes = node.nodes, |
| _node = node; |
| |
| // We can't memoize this, since it's possible that children will be messed |
| // with externally to this class. |
| Iterable<Element> get _iterable => |
| _childNodes.where((n) => n is Element).map<Element>((n) => n as Element); |
| List<Element> get _filtered => |
| new List<Element>.from(_iterable, growable: false); |
| |
| void forEach(void f(Element element)) { |
| // This cannot use the iterator, because operations during iteration might |
| // modify the collection, e.g. addAll might append a node to another parent. |
| _filtered.forEach(f); |
| } |
| |
| void operator []=(int index, Element value) { |
| this[index].replaceWith(value); |
| } |
| |
| set length(int newLength) { |
| final len = this.length; |
| if (newLength >= len) { |
| return; |
| } else if (newLength < 0) { |
| throw new ArgumentError("Invalid list length"); |
| } |
| |
| removeRange(newLength, len); |
| } |
| |
| void add(Element value) { |
| _childNodes.add(value); |
| } |
| |
| void addAll(Iterable<Element> iterable) { |
| for (Element element in iterable) { |
| add(element); |
| } |
| } |
| |
| bool contains(Object needle) { |
| if (needle is! Element) return false; |
| Element element = needle; |
| return element.parentNode == _node; |
| } |
| |
| Iterable<Element> get reversed => _filtered.reversed; |
| |
| void sort([int compare(Element a, Element b)]) { |
| throw new UnsupportedError('Cannot sort filtered list'); |
| } |
| |
| void setRange(int start, int end, Iterable<Element> iterable, |
| [int skipCount = 0]) { |
| throw new UnsupportedError('Cannot setRange on filtered list'); |
| } |
| |
| void fillRange(int start, int end, [Element fillValue]) { |
| throw new UnsupportedError('Cannot fillRange on filtered list'); |
| } |
| |
| void replaceRange(int start, int end, Iterable<Element> iterable) { |
| throw new UnsupportedError('Cannot replaceRange on filtered list'); |
| } |
| |
| void removeRange(int start, int end) { |
| new List.from(_iterable.skip(start).take(end - start)) |
| .forEach((el) => el.remove()); |
| } |
| |
| void clear() { |
| // Currently, ElementList#clear clears even non-element nodes, so we follow |
| // that behavior. |
| _childNodes.clear(); |
| } |
| |
| Element removeLast() { |
| final result = _iterable.last; |
| if (result != null) { |
| result.remove(); |
| } |
| return result; |
| } |
| |
| void insert(int index, Element value) { |
| if (index == length) { |
| add(value); |
| } else { |
| var element = _iterable.elementAt(index); |
| element.parentNode.insertBefore(value, element); |
| } |
| } |
| |
| void insertAll(int index, Iterable<Element> iterable) { |
| if (index == length) { |
| addAll(iterable); |
| } else { |
| var element = _iterable.elementAt(index); |
| element.parentNode.insertAllBefore(iterable, element); |
| } |
| } |
| |
| Element removeAt(int index) { |
| final result = this[index]; |
| result.remove(); |
| return result; |
| } |
| |
| bool remove(Object element) { |
| if (element is! Element) return false; |
| if (contains(element)) { |
| (element as Element).remove(); // Placate the type checker |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| int get length => _iterable.length; |
| Element operator [](int index) => _iterable.elementAt(index); |
| // This cannot use the iterator, because operations during iteration might |
| // modify the collection, e.g. addAll might append a node to another parent. |
| Iterator<Element> get iterator => _filtered.iterator; |
| |
| List<Node> get rawList => _node.childNodes; |
| } |