Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 mdv; | 5 part of mdv; |
| 6 | 6 |
| 7 // This code is a port of Model-Driven-Views: | 7 // This code is a port of Model-Driven-Views: |
| 8 // https://github.com/polymer-project/mdv | 8 // https://github.com/polymer-project/mdv |
| 9 // The code mostly comes from src/template_element.js | 9 // The code mostly comes from src/template_element.js |
| 10 | 10 |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 172 final bool isBinding; | 172 final bool isBinding; |
| 173 | 173 |
| 174 _BindingToken(this.value, {this.isBinding: false}); | 174 _BindingToken(this.value, {this.isBinding: false}); |
| 175 | 175 |
| 176 bool get isText => !isBinding; | 176 bool get isText => !isBinding; |
| 177 } | 177 } |
| 178 | 178 |
| 179 class _TemplateIterator { | 179 class _TemplateIterator { |
| 180 final Element _templateElement; | 180 final Element _templateElement; |
| 181 final List<Node> terminators = []; | 181 final List<Node> terminators = []; |
| 182 final CompoundBinding inputs; | 182 CompoundBinding inputs; |
| 183 List iteratedValue; | 183 List iteratedValue; |
| 184 Object _lastValue; | 184 bool closed = false; |
| 185 | 185 |
| 186 StreamSubscription _sub; | 186 StreamSubscription _sub; |
| 187 StreamSubscription _valueBinding; | |
| 188 | 187 |
| 189 _TemplateIterator(this._templateElement) | 188 _TemplateIterator(this._templateElement) { |
| 190 : inputs = new CompoundBinding(resolveInputs) { | 189 inputs = new CompoundBinding(resolveInputs); |
|
justinfagnani
2013/07/24 00:42:55
why remove this from the initializer list, or make
Jennifer Messerly
2013/07/24 00:52:58
it's sneaky: resolveInputs is no longer static, so
| |
| 191 | |
| 192 _valueBinding = new PathObserver(inputs, 'value').bindSync(valueChanged); | |
| 193 } | 190 } |
| 194 | 191 |
| 195 static Object resolveInputs(Map values) { | 192 resolveInputs(Map values) { |
|
justinfagnani
2013/07/24 00:42:55
void?
Jennifer Messerly
2013/07/24 00:52:58
Done.
Jennifer Messerly
2013/07/24 01:06:55
update: it actually can't be void... CompoundBindi
| |
| 193 if (closed) return; | |
| 194 | |
| 196 if (values.containsKey('if') && !_toBoolean(values['if'])) { | 195 if (values.containsKey('if') && !_toBoolean(values['if'])) { |
| 197 return null; | 196 valueChanged(null); |
| 197 } else if (values.containsKey('repeat')) { | |
| 198 valueChanged(values['repeat']); | |
| 199 } else if (values.containsKey('bind')) { | |
| 200 valueChanged([values['bind']]); | |
| 201 } else { | |
| 202 valueChanged(null); | |
| 198 } | 203 } |
| 199 | |
| 200 if (values.containsKey('repeat')) { | |
| 201 return values['repeat']; | |
| 202 } | |
| 203 | |
| 204 if (values.containsKey('bind')) { | |
| 205 return [values['bind']]; | |
| 206 } | |
| 207 | |
| 208 return null; | |
| 209 } | 204 } |
| 210 | 205 |
| 211 void valueChanged(value) { | 206 void valueChanged(value) { |
| 212 // TODO(jmesserly): should PathObserver do this for us? | 207 if (value is! List) value = null; |
|
justinfagnani
2013/07/24 00:42:55
when do we want to use Iterable over List?
Jennifer Messerly
2013/07/24 00:52:58
good question. I think we should follow up with MD
| |
| 213 var oldValue = _lastValue; | |
| 214 _lastValue = value; | |
| 215 | 208 |
| 216 if (value is! List) { | 209 var oldValue = iteratedValue; |
| 217 value = []; | |
| 218 } | |
| 219 | |
| 220 unobserve(); | 210 unobserve(); |
| 221 iteratedValue = value; | 211 iteratedValue = value; |
| 222 | 212 |
| 223 if (value is Observable) { | 213 if (iteratedValue is Observable) { |
| 224 _sub = value.changes.listen(_handleChanges); | 214 _sub = iteratedValue.changes.listen(_handleChanges); |
| 225 } | 215 } |
| 226 | 216 |
| 227 int addedCount = iteratedValue.length; | 217 var splices = calculateSplices( |
| 228 var removedCount = oldValue is List ? (oldValue as List).length : 0; | 218 iteratedValue != null ? iteratedValue : [], |
| 229 if (addedCount == 0 && removedCount == 0) return; // nothing to do. | 219 oldValue != null ? oldValue : []); |
| 230 | 220 |
| 231 _handleChanges([new ListChangeRecord(0, addedCount: addedCount, | 221 if (splices.length > 0) _handleChanges(splices); |
| 232 removedCount: removedCount)]); | 222 |
| 223 if (inputs.length == 0) { | |
| 224 close(); | |
| 225 _mdv(_templateElement)._templateIterator = null; | |
| 226 } | |
| 233 } | 227 } |
| 234 | 228 |
| 235 Node getTerminatorAt(int index) { | 229 Node getTerminatorAt(int index) { |
| 236 if (index == -1) return _templateElement; | 230 if (index == -1) return _templateElement; |
| 237 var terminator = terminators[index]; | 231 var terminator = terminators[index]; |
| 238 if (terminator is Element && (terminator as Element).isTemplate && | 232 if (terminator is Element && (terminator as Element).isTemplate && |
| 239 !identical(terminator, _templateElement)) { | 233 !identical(terminator, _templateElement)) { |
| 240 var subIterator = _mdv(terminator)._templateIterator; | 234 var subIterator = _mdv(terminator)._templateIterator; |
| 241 if (subIterator != null) { | 235 if (subIterator != null) { |
| 242 return subIterator.getTerminatorAt(subIterator.terminators.length - 1); | 236 return subIterator.getTerminatorAt(subIterator.terminators.length - 1); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 288 var instanceNodes = instanceCache.remove(model); | 282 var instanceNodes = instanceCache.remove(model); |
| 289 if (instanceNodes != null) return instanceNodes; | 283 if (instanceNodes != null) return instanceNodes; |
| 290 | 284 |
| 291 var fragment = _templateElement.createInstance(model, syntax); | 285 var fragment = _templateElement.createInstance(model, syntax); |
| 292 instanceNodes = fragment.nodes.toList(); | 286 instanceNodes = fragment.nodes.toList(); |
| 293 fragment.nodes.clear(); | 287 fragment.nodes.clear(); |
| 294 return instanceNodes; | 288 return instanceNodes; |
| 295 } | 289 } |
| 296 | 290 |
| 297 void _handleChanges(Iterable<ChangeRecord> splices) { | 291 void _handleChanges(Iterable<ChangeRecord> splices) { |
| 292 if (closed) return; | |
| 293 | |
| 298 splices = splices.where((s) => s is ListChangeRecord); | 294 splices = splices.where((s) => s is ListChangeRecord); |
| 299 | 295 |
| 300 var template = _templateElement; | 296 var template = _templateElement; |
| 301 var syntax = template.attributes['syntax']; | 297 var syntax = template.attributes['syntax']; |
| 302 | 298 |
| 303 if (template.parentNode == null || template.document.window == null) { | 299 if (template.parentNode == null || template.document.window == null) { |
| 304 abandon(); | 300 close(); |
| 305 // TODO(jmesserly): MDV calls templateIteratorTable.delete(this) here, | 301 // TODO(jmesserly): MDV calls templateIteratorTable.delete(this) here, |
| 306 // but I think that's a no-op because only nodes are used as keys. | 302 // but I think that's a no-op because only nodes are used as keys. |
| 307 // See https://github.com/Polymer/mdv/pull/114. | 303 // See https://github.com/Polymer/mdv/pull/114. |
| 308 return; | 304 return; |
| 309 } | 305 } |
| 310 | 306 |
| 311 // TODO(jmesserly): IdentityMap matches JS semantics, but it's O(N) right | 307 // TODO(jmesserly): IdentityMap matches JS semantics, but it's O(N) right |
| 312 // now. See http://dartbug.com/4161. | 308 // now. See http://dartbug.com/4161. |
| 313 var instanceCache = new IdentityMap(); | 309 var instanceCache = new IdentityMap(); |
| 314 var removeDelta = 0; | 310 var removeDelta = 0; |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 339 instanceNodes.forEach(_unbindAllRecursively); | 335 instanceNodes.forEach(_unbindAllRecursively); |
| 340 } | 336 } |
| 341 } | 337 } |
| 342 | 338 |
| 343 void unobserve() { | 339 void unobserve() { |
| 344 if (_sub == null) return; | 340 if (_sub == null) return; |
| 345 _sub.cancel(); | 341 _sub.cancel(); |
| 346 _sub = null; | 342 _sub = null; |
| 347 } | 343 } |
| 348 | 344 |
| 349 void abandon() { | 345 void close() { |
| 346 if (closed) return; | |
| 347 | |
| 350 unobserve(); | 348 unobserve(); |
| 351 _valueBinding.cancel(); | |
| 352 terminators.clear(); | 349 terminators.clear(); |
| 353 inputs.dispose(); | 350 inputs.dispose(); |
| 351 closed = true; | |
| 354 } | 352 } |
| 355 | 353 |
| 356 static void _unbindAllRecursively(Node node) { | 354 static void _unbindAllRecursively(Node node) { |
| 357 var nodeExt = _mdv(node); | 355 var nodeExt = _mdv(node); |
| 358 nodeExt._templateInstance = null; | 356 nodeExt._templateInstance = null; |
| 359 if (node is Element && (node as Element).isTemplate) { | 357 if (node is Element && (node as Element).isTemplate) { |
| 360 // Make sure we stop observing when we remove an element. | 358 // Make sure we stop observing when we remove an element. |
| 361 var templateIterator = nodeExt._templateIterator; | 359 var templateIterator = nodeExt._templateIterator; |
| 362 if (templateIterator != null) { | 360 if (templateIterator != null) { |
| 363 templateIterator.abandon(); | 361 templateIterator.close(); |
| 364 nodeExt._templateIterator = null; | 362 nodeExt._templateIterator = null; |
| 365 } | 363 } |
| 366 } | 364 } |
| 367 | 365 |
| 368 _nodeOrCustom(node).unbindAll(); | 366 _nodeOrCustom(node).unbindAll(); |
| 369 for (var c = node.firstChild; c != null; c = c.nextNode) { | 367 for (var c = node.firstChild; c != null; c = c.nextNode) { |
| 370 _unbindAllRecursively(c); | 368 _unbindAllRecursively(c); |
| 371 } | 369 } |
| 372 } | 370 } |
| 373 } | 371 } |
| OLD | NEW |