blob: fba6ad73187263f58b17b3ab2e7515b18855bdec [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 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;
}