blob: 64326a47df1a2ba87de7b9b046f41bb7f886f5a7 [file] [log] [blame]
// 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 descendants in the document tree,
* filtered so that only elements are in the collection.
*/
class FilteredElementList<T extends Element> extends ListBase<T> {
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.
//
// TODO(nweiz): we don't always need to create a new list. For example
// forEach, every, any, ... could directly work on the _childNodes.
List<Element> get _filtered =>
new List<Element>.from(_childNodes.where((n) => n is Element));
void forEach(void f(Element element)) {
_filtered.forEach(f);
}
void operator []=(int index, Element value) {
this[index].replaceWith(value);
}
void 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) {
_filtered.sublist(start, end).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 = this.last;
if (result != null) {
result.remove();
}
return result;
}
void insert(int index, Element value) {
_childNodes.insert(index, value);
}
void insertAll(int index, Iterable<Element> iterable) {
_childNodes.insertAll(index, iterable);
}
Element removeAt(int index) {
final result = this[index];
result.remove();
return result;
}
bool remove(Object element) {
if (element is! Element) return false;
for (int i = 0; i < length; i++) {
Element indexElement = this[i];
if (identical(indexElement, element)) {
indexElement.remove();
return true;
}
}
return false;
}
int get length => _filtered.length;
Element operator [](int index) => _filtered[index];
Iterator<Element> get iterator => _filtered.iterator;
}