// Copyright (c) 2012, 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 $LIBRARYNAME; /** * Lazy implementation of the child nodes of an element that does not request * the actual child nodes of an element until strictly necessary greatly * improving performance for the typical cases where it is not required. */ class _ChildNodeListLazy extends ListBase implements NodeListWrapper { final Node _this; _ChildNodeListLazy(this._this); Node get first { Node$NULLABLE result = JS('Node|Null', '#.firstChild', _this); if (result == null) throw new StateError("No elements"); return result; } Node get last { Node$NULLABLE result = JS('Node|Null', '#.lastChild', _this); if (result == null) throw new StateError("No elements"); return result; } Node get single { int l = this.length; if (l == 0) throw new StateError("No elements"); if (l > 1) throw new StateError("More than one element"); return JS('Node|Null', '#.firstChild', _this)$NULLASSERT; } void add(Node value) { _this.append(value); } void addAll(Iterable iterable) { if (iterable is _ChildNodeListLazy) { _ChildNodeListLazy otherList = iterable; if (!identical(otherList._this, _this)) { // Optimized route for copying between nodes. for (var i = 0, len = otherList.length; i < len; ++i) { _this.append(otherList._this.firstChild$NULLASSERT); } } return; } for (Node node in iterable) { _this.append(node); } } void insert(int index, Node node) { if (index < 0 || index > length) { throw new RangeError.range(index, 0, length); } if (index == length) { _this.append(node); } else { _this.insertBefore(node, this[index]); } } void insertAll(int index, Iterable iterable) { if (index == length) { addAll(iterable); } else { var item = this[index]; _this.insertAllBefore(iterable, item); } } void setAll(int index, Iterable iterable) { throw new UnsupportedError("Cannot setAll on Node list"); } Node removeLast() { final result = last; if (result != null) { _this._removeChild(result); } return result; } Node removeAt(int index) { var result = this[index]; if (result != null) { _this._removeChild(result); } return result; } bool remove(Object$NULLABLE object) { if (object is! Node) return false; Node node = object; if (!identical(_this, node.parentNode)) return false; _this._removeChild(node); return true; } void _filter(bool test(Node node), bool removeMatching) { // This implementation of removeWhere/retainWhere is more efficient // than the default in ListBase. Child nodes can be removed in constant // time. Node$NULLABLE child = _this.firstChild; while (child != null) { Node$NULLABLE nextChild = child.nextNode; if (test(child) == removeMatching) { _this._removeChild(child); } child = nextChild; } } void removeWhere(bool test(Node node)) { _filter(test, true); } void retainWhere(bool test(Node node)) { _filter(test, false); } void clear() { _this._clearChildren(); } void operator []=(int index, Node value) { _this._replaceChild(value, this[index]); } Iterator get iterator => _this.childNodes.iterator; // From List: // TODO(jacobr): this could be implemented for child node lists. // The exception we throw here is misleading. void sort([Comparator$NULLABLE compare]) { throw new UnsupportedError("Cannot sort Node list"); } void shuffle([Random$NULLABLE random]) { throw new UnsupportedError("Cannot shuffle Node list"); } // FIXME: implement these. void setRange(int start, int end, Iterable iterable, [int skipCount = 0]) { throw new UnsupportedError("Cannot setRange on Node list"); } void fillRange(int start, int end, [Node$NULLABLE fill]) { throw new UnsupportedError("Cannot fillRange on Node list"); } void removeRange(int start, int end) { throw new UnsupportedError("Cannot removeRange on Node list"); } // -- end List mixins. // TODO(jacobr): benchmark whether this is more efficient or whether caching // a local copy of childNodes is more efficient. int get length => _this.childNodes.length; set length(int value) { throw new UnsupportedError( "Cannot set length on immutable List."); } Node operator[](int index) => _this.childNodes[index]; List get rawList => _this.childNodes; } $(ANNOTATIONS)$(NATIVESPEC)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS { // Custom element created callback. Node._created() : super._created(); /** * A modifiable list of this node's children. */ List get nodes { return new _ChildNodeListLazy(this); } set nodes(Iterable value) { // Copy list first since we don't want liveness during iteration. // TODO(jacobr): there is a better way to do this. var copy = value.toList(); text = ''; for (Node node in copy) { append(node); } } /** * Removes this node from the DOM. */ void remove() { // TODO(jacobr): should we throw an exception if parent is already null? // TODO(vsm): Use the native remove when available. if (this.parentNode != null) { final Node parent = this.parentNode$NULLASSERT; parent._removeChild(this); } } /** * Replaces this node with another node. */ Node replaceWith(Node otherNode) { try { final Node parent = this.parentNode$NULLASSERT; parent._replaceChild(otherNode, this); } catch (e) {} return this; } /** * Inserts all of the nodes into this node directly before refChild. * * See also: * * * [insertBefore] */ void insertAllBefore(Iterable newNodes, Node refChild) { if (newNodes is _ChildNodeListLazy) { _ChildNodeListLazy otherList = newNodes; if (identical(otherList._this, this)) { throw new ArgumentError(newNodes); } // Optimized route for copying between nodes. for (var i = 0, len = otherList.length; i < len; ++i) { this.insertBefore(otherList._this.firstChild$NULLASSERT, refChild); } } else { for (var node in newNodes) { this.insertBefore(node, refChild); } } } void _clearChildren() { while (firstChild != null) { _removeChild(firstChild$NULLASSERT); } } /** * Print out a String representation of this Node. */ String toString() { String$NULLABLE value = nodeValue; // Fetch DOM Node property once. return value == null ? super.toString() : value; } /** * A list of this node's children. * * ## Other resources * * * [Node.childNodes](https://developer.mozilla.org/en-US/docs/Web/API/Node.childNodes) * from MDN. */ @Returns('NodeList') @Creates('NodeList') List get childNodes native; $!MEMBERS }