| 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. |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 _this.$dom_removeChild(node); | 114 _this.$dom_removeChild(node); |
| 115 return true; | 115 return true; |
| 116 } | 116 } |
| 117 | 117 |
| 118 void _filter(bool test(Node node), bool removeMatching) { | 118 void _filter(bool test(Node node), bool removeMatching) { |
| 119 // This implementation of removeWhere/retainWhere is more efficient | 119 // This implementation of removeWhere/retainWhere is more efficient |
| 120 // than the default in ListBase. Child nodes can be removed in constant | 120 // than the default in ListBase. Child nodes can be removed in constant |
| 121 // time. | 121 // time. |
| 122 Node child = _this.$dom_firstChild; | 122 Node child = _this.$dom_firstChild; |
| 123 while (child != null) { | 123 while (child != null) { |
| 124 Node nextChild = child.nextSibling; | 124 Node nextChild = child.nextNode; |
| 125 if (test(child) == removeMatching) { | 125 if (test(child) == removeMatching) { |
| 126 _this.$dom_removeChild(child); | 126 _this.$dom_removeChild(child); |
| 127 } | 127 } |
| 128 child = nextChild; | 128 child = nextChild; |
| 129 } | 129 } |
| 130 } | 130 } |
| 131 | 131 |
| 132 void removeWhere(bool test(Node node)) { | 132 void removeWhere(bool test(Node node)) { |
| 133 _filter(test, true); | 133 _filter(test, true); |
| 134 } | 134 } |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 238 // Should use $dom_firstChild, Bug 8886. | 238 // Should use $dom_firstChild, Bug 8886. |
| 239 this.insertBefore(newNodes[0], refChild); | 239 this.insertBefore(newNodes[0], refChild); |
| 240 } | 240 } |
| 241 } else { | 241 } else { |
| 242 for (var node in newNodes) { | 242 for (var node in newNodes) { |
| 243 this.insertBefore(node, refChild); | 243 this.insertBefore(node, refChild); |
| 244 } | 244 } |
| 245 } | 245 } |
| 246 } | 246 } |
| 247 | 247 |
| 248 // Note that this may either be the locally set model or a cached value | |
| 249 // of the inherited model. This is cached to minimize model change | |
| 250 // notifications. | |
| 251 $if DART2JS | |
| 252 @Creates('Null') | |
| 253 $endif | |
| 254 var _model; | |
| 255 bool _hasLocalModel; | |
| 256 Set<StreamController<Node>> _modelChangedStreams; | |
| 257 | |
| 258 /** | |
| 259 * The data model which is inherited through the tree. | |
| 260 * | |
| 261 * Setting this will propagate the value to all descendant nodes. If the | |
| 262 * model is not set on this node then it will be inherited from ancestor | |
| 263 * nodes. | |
| 264 * | |
| 265 * Currently this does not support propagation through Shadow DOMs. | |
| 266 * | |
| 267 * [clearModel] must be used to remove the model property from this node | |
| 268 * and have the model inherit from ancestor nodes. | |
| 269 */ | |
| 270 @Experimental | |
| 271 get model { | |
| 272 // If we have a change handler then we've cached the model locally. | |
| 273 if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) { | |
| 274 return _model; | |
| 275 } | |
| 276 // Otherwise start looking up the tree. | |
| 277 for (var node = this; node != null; node = node.parentNode) { | |
| 278 if (node._hasLocalModel == true) { | |
| 279 return node._model; | |
| 280 } | |
| 281 } | |
| 282 return null; | |
| 283 } | |
| 284 | |
| 285 @Experimental | |
| 286 void set model(value) { | |
| 287 var changed = model != value; | |
| 288 _model = value; | |
| 289 _hasLocalModel = true; | |
| 290 _ModelTreeObserver.initialize(); | |
| 291 | |
| 292 if (changed) { | |
| 293 if (_modelChangedStreams != null && !_modelChangedStreams.isEmpty) { | |
| 294 _modelChangedStreams.toList().forEach((stream) => stream.add(this)); | |
| 295 } | |
| 296 // Propagate new model to all descendants. | |
| 297 _ModelTreeObserver.propagateModel(this, value, false); | |
| 298 } | |
| 299 } | |
| 300 | |
| 301 /** | |
| 302 * Clears the locally set model and makes this model be inherited from parent | |
| 303 * nodes. | |
| 304 */ | |
| 305 @Experimental | |
| 306 void clearModel() { | |
| 307 if (_hasLocalModel == true) { | |
| 308 _hasLocalModel = false; | |
| 309 | |
| 310 // Propagate new model to all descendants. | |
| 311 if (parentNode != null) { | |
| 312 _ModelTreeObserver.propagateModel(this, parentNode.model, false); | |
| 313 } else { | |
| 314 _ModelTreeObserver.propagateModel(this, null, false); | |
| 315 } | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 /** | |
| 320 * Get a stream of models, whenever the model changes. | |
| 321 */ | |
| 322 Stream<Node> get onModelChanged { | |
| 323 if (_modelChangedStreams == null) { | |
| 324 _modelChangedStreams = new Set<StreamController<Node>>(); | |
| 325 } | |
| 326 var controller; | |
| 327 controller = new StreamController( | |
| 328 onListen: () { _modelChangedStreams.add(controller); }, | |
| 329 onCancel: () { _modelChangedStreams.remove(controller); }); | |
| 330 return controller.stream; | |
| 331 } | |
| 332 | |
| 333 /** | 248 /** |
| 334 * Print out a String representation of this Node. | 249 * Print out a String representation of this Node. |
| 335 */ | 250 */ |
| 336 String toString() => localName == null ? | 251 String toString() => localName == null ? |
| 337 (nodeValue == null ? super.toString() : nodeValue) : localName; | 252 (nodeValue == null ? super.toString() : nodeValue) : localName; |
| 338 | 253 |
| 254 /** |
| 255 * Binds the attribute [name] to the [path] of the [model]. |
| 256 * Path is a String of accessors such as `foo.bar.baz`. |
| 257 */ |
| 258 @Experimental |
| 259 void bind(String name, model, String path) { |
| 260 // TODO(jmesserly): should we throw instead? |
| 261 window.console.error('Unhandled binding to Node: ' |
| 262 '$this $name $model $path'); |
| 263 } |
| 264 |
| 265 /** Unbinds the attribute [name]. */ |
| 266 @Experimental |
| 267 void unbind(String name) {} |
| 268 |
| 269 /** Unbinds all bound attributes. */ |
| 270 @Experimental |
| 271 void unbindAll() {} |
| 272 |
| 273 TemplateInstance _templateInstance; |
| 274 |
| 275 // TODO(arv): Consider storing all "NodeRareData" on a single object? |
| 276 int __instanceTerminatorCount; |
| 277 int get _instanceTerminatorCount { |
| 278 if (__instanceTerminatorCount == null) return 0; |
| 279 return __instanceTerminatorCount; |
| 280 } |
| 281 set _instanceTerminatorCount(int value) { |
| 282 if (value == 0) value = null; |
| 283 __instanceTerminatorCount = value; |
| 284 } |
| 285 |
| 286 /** Gets the template instance that instantiated this node, if any. */ |
| 287 @Experimental |
| 288 TemplateInstance get templateInstance => |
| 289 _templateInstance != null ? _templateInstance : |
| 290 (parent != null ? parent.templateInstance : null); |
| 291 |
| 339 $!MEMBERS | 292 $!MEMBERS |
| 340 } | 293 } |
| OLD | NEW |