| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of $LIBRARYNAME; | 5 part of $LIBRARYNAME; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Lazy implementation of the child nodes of an element that does not request | 8 * Lazy implementation of the child nodes of an element that does not request |
| 9 * the actual child nodes of an element until strictly necessary greatly | 9 * the actual child nodes of an element until strictly necessary greatly |
| 10 * improving performance for the typical cases where it is not required. | 10 * improving performance for the typical cases where it is not required. |
| 11 */ | 11 */ |
| 12 class _ChildNodeListLazy implements List { | 12 class _ChildNodeListLazy extends ListBase<Node> { |
| 13 final Node _this; | 13 final Node _this; |
| 14 | 14 |
| 15 _ChildNodeListLazy(this._this); | 15 _ChildNodeListLazy(this._this); |
| 16 | 16 |
| 17 | 17 |
| 18 $if DART2JS | 18 $if DART2JS |
| 19 Node get first { | 19 Node get first { |
| 20 Node result = JS('Node|Null', '#.firstChild', _this); | 20 Node result = JS('Node|Null', '#.firstChild', _this); |
| 21 if (result == null) throw new StateError("No elements"); | 21 if (result == null) throw new StateError("No elements"); |
| 22 return result; | 22 return result; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 44 return result; | 44 return result; |
| 45 } | 45 } |
| 46 Node get single { | 46 Node get single { |
| 47 int l = this.length; | 47 int l = this.length; |
| 48 if (l == 0) throw new StateError("No elements"); | 48 if (l == 0) throw new StateError("No elements"); |
| 49 if (l > 1) throw new StateError("More than one element"); | 49 if (l > 1) throw new StateError("More than one element"); |
| 50 return _this.$dom_firstChild; | 50 return _this.$dom_firstChild; |
| 51 } | 51 } |
| 52 $endif | 52 $endif |
| 53 | 53 |
| 54 Node min([int compare(Node a, Node b)]) { | |
| 55 return IterableMixinWorkaround.min(this, compare); | |
| 56 } | |
| 57 | |
| 58 Node max([int compare(Node a, Node b)]) { | |
| 59 return IterableMixinWorkaround.max(this, compare); | |
| 60 } | |
| 61 | |
| 62 void add(Node value) { | 54 void add(Node value) { |
| 63 _this.append(value); | 55 _this.append(value); |
| 64 } | 56 } |
| 65 | 57 |
| 66 void addAll(Iterable<Node> iterable) { | 58 void addAll(Iterable<Node> iterable) { |
| 67 if (iterable is _ChildNodeListLazy) { | 59 if (iterable is _ChildNodeListLazy) { |
| 68 if (!identical(iterable._this, _this)) { | 60 if (!identical(iterable._this, _this)) { |
| 69 // Optimized route for copying between nodes. | 61 // Optimized route for copying between nodes. |
| 70 for (var i = 0, len = iterable.length; i < len; ++i) { | 62 for (var i = 0, len = iterable.length; i < len; ++i) { |
| 71 // Should use $dom_firstChild, Bug 8886. | 63 // Should use $dom_firstChild, Bug 8886. |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 107 } | 99 } |
| 108 | 100 |
| 109 void remove(Object object) { | 101 void remove(Object object) { |
| 110 if (object is! Node) return; | 102 if (object is! Node) return; |
| 111 Node node = object; | 103 Node node = object; |
| 112 if (!identical(_this, node.parentNode)) return; | 104 if (!identical(_this, node.parentNode)) return; |
| 113 _this.$dom_removeChild(node); | 105 _this.$dom_removeChild(node); |
| 114 } | 106 } |
| 115 | 107 |
| 116 void removeAll(Iterable elements) { | 108 void removeAll(Iterable elements) { |
| 117 IterableMixinWorkaround.removeAll(this, elements); | 109 // This is not using the default removeAll from ListBase because |
| 110 // DOM nodes can be efficiently removed in constant time. |
| 111 for (var element in elements) { |
| 112 remove(element); |
| 113 } |
| 114 } |
| 115 |
| 116 void _filter(bool test(Node node), bool removeMatching) { |
| 117 // This implementation of removeWhere/retainWhere is more efficient |
| 118 // than the default in ListBase. Child nodes can be removed in constant |
| 119 // time. |
| 120 Node child = _this.$dom_firstChild; |
| 121 while (child != null) { |
| 122 Node nextChild = child.nextSibling; |
| 123 if (test(child) == removeMatching) { |
| 124 _this.$dom_removeChild(child); |
| 125 } |
| 126 child = nextChild; |
| 127 } |
| 118 } | 128 } |
| 119 | 129 |
| 120 void retainAll(Iterable elements) { | 130 void retainAll(Iterable elements) { |
| 121 IterableMixinWorkaround.retainAll(this, elements); | 131 Set retainSet = (elements is Set) ? elements : elements.toSet(); |
| 132 _filter(retainSet.contains, false); |
| 122 } | 133 } |
| 123 | 134 |
| 124 void removeWhere(bool test(Node node)) { | 135 void removeWhere(bool test(Node node)) { |
| 125 IterableMixinWorkaround.removeWhere(this, test); | 136 _filter(test, true); |
| 126 } | 137 } |
| 127 | 138 |
| 128 void retainWhere(bool test(Node node)) { | 139 void retainWhere(bool test(Node node)) { |
| 129 IterableMixinWorkaround.retainWhere(this, test); | 140 _filter(test, false); |
| 130 } | 141 } |
| 131 | 142 |
| 132 void clear() { | 143 void clear() { |
| 133 _this.text = ''; | 144 _this.text = ''; |
| 134 } | 145 } |
| 135 | 146 |
| 136 void operator []=(int index, Node value) { | 147 void operator []=(int index, Node value) { |
| 137 _this.$dom_replaceChild(value, this[index]); | 148 _this.$dom_replaceChild(value, this[index]); |
| 138 } | 149 } |
| 139 | 150 |
| 140 Iterator<Node> get iterator => _this.$dom_childNodes.iterator; | 151 Iterator<Node> get iterator => _this.$dom_childNodes.iterator; |
| 141 | 152 |
| 142 // TODO(jacobr): We can implement these methods much more efficiently by | |
| 143 // looking up the nodeList only once instead of once per iteration. | |
| 144 bool contains(Node element) => IterableMixinWorkaround.contains(this, element)
; | |
| 145 | |
| 146 void forEach(void f(Node element)) => IterableMixinWorkaround.forEach(this, f)
; | |
| 147 | |
| 148 dynamic reduce(dynamic initialValue, | |
| 149 dynamic combine(dynamic previousValue, Node element)) { | |
| 150 return IterableMixinWorkaround.reduce(this, initialValue, combine); | |
| 151 } | |
| 152 | |
| 153 dynamic fold(dynamic initialValue, | |
| 154 dynamic combine(dynamic previousValue, Node element)) { | |
| 155 return IterableMixinWorkaround.fold(this, initialValue, combine); | |
| 156 } | |
| 157 | |
| 158 String join([String separator]) { | |
| 159 return IterableMixinWorkaround.joinList(this, separator); | |
| 160 } | |
| 161 | |
| 162 Iterable map(f(Node element)) { | |
| 163 return IterableMixinWorkaround.mapList(this, f); | |
| 164 } | |
| 165 | |
| 166 Iterable<Node> where(bool f(Node element)) { | |
| 167 return IterableMixinWorkaround.where(this, f); | |
| 168 } | |
| 169 | |
| 170 Iterable expand(Iterable f(Node element)) { | |
| 171 return IterableMixinWorkaround.expand(this, f); | |
| 172 } | |
| 173 | |
| 174 bool every(bool f(Node element)) => IterableMixinWorkaround.every(this, f); | |
| 175 | |
| 176 bool any(bool f(Node element)) => IterableMixinWorkaround.any(this, f); | |
| 177 | |
| 178 List<Node> toList({ bool growable: true }) => | 153 List<Node> toList({ bool growable: true }) => |
| 179 new List<Node>.from(this, growable: growable); | 154 new List<Node>.from(this, growable: growable); |
| 180 Set<Node> toSet() => new Set<Node>.from(this); | 155 Set<Node> toSet() => new Set<Node>.from(this); |
| 181 | 156 |
| 182 bool get isEmpty => this.length == 0; | 157 bool get isEmpty => this.length == 0; |
| 183 | 158 |
| 184 // From List<Node>: | 159 // From List<Node>: |
| 185 | 160 |
| 186 Iterable<Node> take(int n) { | |
| 187 return IterableMixinWorkaround.takeList(this, n); | |
| 188 } | |
| 189 | |
| 190 Iterable<Node> takeWhile(bool test(Node value)) { | |
| 191 return IterableMixinWorkaround.takeWhile(this, test); | |
| 192 } | |
| 193 | |
| 194 Iterable<Node> skip(int n) { | |
| 195 return IterableMixinWorkaround.skipList(this, n); | |
| 196 } | |
| 197 | |
| 198 Iterable<Node> skipWhile(bool test(Node value)) { | |
| 199 return IterableMixinWorkaround.skipWhile(this, test); | |
| 200 } | |
| 201 | |
| 202 Node firstWhere(bool test(Node value), {Node orElse()}) { | |
| 203 return IterableMixinWorkaround.firstWhere(this, test, orElse); | |
| 204 } | |
| 205 | |
| 206 Node lastWhere(bool test(Node value), {Node orElse()}) { | |
| 207 return IterableMixinWorkaround.lastWhereList(this, test, orElse); | |
| 208 } | |
| 209 | |
| 210 Node singleWhere(bool test(Node value)) { | |
| 211 return IterableMixinWorkaround.singleWhere(this, test); | |
| 212 } | |
| 213 | |
| 214 Node elementAt(int index) { | |
| 215 return this[index]; | |
| 216 } | |
| 217 | |
| 218 Iterable<Node> get reversed { | |
| 219 return IterableMixinWorkaround.reversedList(this); | |
| 220 } | |
| 221 | |
| 222 // TODO(jacobr): this could be implemented for child node lists. | 161 // TODO(jacobr): this could be implemented for child node lists. |
| 223 // The exception we throw here is misleading. | 162 // The exception we throw here is misleading. |
| 224 void sort([int compare(Node a, Node b)]) { | 163 void sort([int compare(Node a, Node b)]) { |
| 225 throw new UnsupportedError("Cannot sort immutable List."); | 164 throw new UnsupportedError("Cannot sort immutable List."); |
| 226 } | 165 } |
| 227 | 166 |
| 228 int indexOf(Node element, [int start = 0]) => | |
| 229 Lists.indexOf(this, element, start, this.length); | |
| 230 | |
| 231 int lastIndexOf(Node element, [int start = 0]) => | |
| 232 Lists.lastIndexOf(this, element, start); | |
| 233 | |
| 234 // FIXME: implement these. | 167 // FIXME: implement these. |
| 235 void setRange(int start, int rangeLength, List<Node> from, [int startFrom]) { | 168 void setRange(int start, int rangeLength, List<Node> from, [int startFrom]) { |
| 236 throw new UnsupportedError( | 169 throw new UnsupportedError( |
| 237 "Cannot setRange on immutable List."); | 170 "Cannot setRange on immutable List."); |
| 238 } | 171 } |
| 239 void removeRange(int start, int rangeLength) { | 172 void removeRange(int start, int rangeLength) { |
| 240 throw new UnsupportedError( | 173 throw new UnsupportedError( |
| 241 "Cannot removeRange on immutable List."); | 174 "Cannot removeRange on immutable List."); |
| 242 } | 175 } |
| 243 void insertRange(int start, int rangeLength, [Node initialValue]) { | 176 void insertRange(int start, int rangeLength, [Node initialValue]) { |
| (...skipping 19 matching lines...) Expand all Loading... |
| 263 // TODO(jacobr): benchmark whether this is more efficient or whether caching | 196 // TODO(jacobr): benchmark whether this is more efficient or whether caching |
| 264 // a local copy of $dom_childNodes is more efficient. | 197 // a local copy of $dom_childNodes is more efficient. |
| 265 int get length => _this.$dom_childNodes.length; | 198 int get length => _this.$dom_childNodes.length; |
| 266 | 199 |
| 267 void set length(int value) { | 200 void set length(int value) { |
| 268 throw new UnsupportedError( | 201 throw new UnsupportedError( |
| 269 "Cannot set length on immutable List."); | 202 "Cannot set length on immutable List."); |
| 270 } | 203 } |
| 271 | 204 |
| 272 Node operator[](int index) => _this.$dom_childNodes[index]; | 205 Node operator[](int index) => _this.$dom_childNodes[index]; |
| 273 | |
| 274 Map<int, Node> asMap() => IterableMixinWorkaround.asMapList(this); | |
| 275 } | 206 } |
| 276 | 207 |
| 277 $(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | 208 $(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { |
| 278 List<Node> get nodes { | 209 List<Node> get nodes { |
| 279 return new _ChildNodeListLazy(this); | 210 return new _ChildNodeListLazy(this); |
| 280 } | 211 } |
| 281 | 212 |
| 282 void set nodes(Collection<Node> value) { | 213 void set nodes(Collection<Node> value) { |
| 283 // Copy list first since we don't want liveness during iteration. | 214 // Copy list first since we don't want liveness during iteration. |
| 284 // TODO(jacobr): there is a better way to do this. | 215 // TODO(jacobr): there is a better way to do this. |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 417 */ | 348 */ |
| 418 Stream<Node> get onModelChanged { | 349 Stream<Node> get onModelChanged { |
| 419 if (_modelChangedStream == null) { | 350 if (_modelChangedStream == null) { |
| 420 // Ensure the model is cached locally to minimize change notifications. | 351 // Ensure the model is cached locally to minimize change notifications. |
| 421 _model = model; | 352 _model = model; |
| 422 _modelChangedStream = new StreamController.broadcast(); | 353 _modelChangedStream = new StreamController.broadcast(); |
| 423 } | 354 } |
| 424 return _modelChangedStream.stream; | 355 return _modelChangedStream.stream; |
| 425 } | 356 } |
| 426 | 357 |
| 427 /** | 358 /** |
| 428 * Print out a String representation of this Node. | 359 * Print out a String representation of this Node. |
| 429 */ | 360 */ |
| 430 String toString() => localName == null ? | 361 String toString() => localName == null ? |
| 431 (nodeValue == null ? super.toString() : nodeValue) : localName; | 362 (nodeValue == null ? super.toString() : nodeValue) : localName; |
| 432 | 363 |
| 433 $!MEMBERS | 364 $!MEMBERS |
| 434 } | 365 } |
| OLD | NEW |