| 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 /** | 5 /** |
| 6 * A binding delegate used with Polymer elements that | 6 * A binding delegate used with Polymer elements that |
| 7 * allows for complex binding expressions, including | 7 * allows for complex binding expressions, including |
| 8 * property access, function invocation, | 8 * property access, function invocation, |
| 9 * list/map indexing, and two-way filtering. | 9 * list/map indexing, and two-way filtering. |
| 10 * | 10 * |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 try { | 271 try { |
| 272 var value = eval(expr, scope); | 272 var value = eval(expr, scope); |
| 273 return (converter == null) ? value : converter(value); | 273 return (converter == null) ? value : converter(value); |
| 274 } catch (e, s) { | 274 } catch (e, s) { |
| 275 new Completer().completeError( | 275 new Completer().completeError( |
| 276 "Error evaluating expression '$expr': $e", s); | 276 "Error evaluating expression '$expr': $e", s); |
| 277 } | 277 } |
| 278 return null; | 278 return null; |
| 279 } | 279 } |
| 280 | 280 |
| 281 _check(v, {bool skipChanges: false}) { | 281 bool _convertAndCheck(newValue, {bool skipChanges: false}) { |
| 282 var oldValue = _value; | 282 var oldValue = _value; |
| 283 _value = _converter(v); | 283 _value = _converter(newValue); |
| 284 |
| 284 if (!skipChanges && _callback != null && oldValue != _value) { | 285 if (!skipChanges && _callback != null && oldValue != _value) { |
| 285 _callback(_value); | 286 _callback(_value); |
| 287 return true; |
| 286 } | 288 } |
| 289 return false; |
| 287 } | 290 } |
| 288 | 291 |
| 292 // TODO(jmesserly): this should discard changes, but it caused |
| 293 // a strange infinite loop in one of the bindings_tests. |
| 294 // For now skipping the test. See http://dartbug.com/19105. |
| 289 get value { | 295 get value { |
| 290 // if there's a callback, then _value has been set, if not we need to | 296 // if there's a callback, then _value has been set, if not we need to |
| 291 // force an evaluation | 297 // force an evaluation |
| 292 if (_callback != null) return _value; | 298 if (_callback != null) return _value; |
| 293 return _oneTime(_expr, _scope, _converter); | 299 return _oneTime(_expr, _scope, _converter); |
| 294 } | 300 } |
| 295 | 301 |
| 296 set value(v) { | 302 set value(v) { |
| 297 try { | 303 try { |
| 298 var newValue = assign(_expr, v, _scope, checkAssignability: false); | 304 var newValue = assign(_expr, v, _scope, checkAssignability: false); |
| 299 _check(newValue, skipChanges: true); | 305 _convertAndCheck(newValue); |
| 300 } catch (e, s) { | 306 } catch (e, s) { |
| 301 new Completer().completeError( | 307 new Completer().completeError( |
| 302 "Error evaluating expression '$_expr': $e", s); | 308 "Error evaluating expression '$_expr': $e", s); |
| 303 } | 309 } |
| 304 } | 310 } |
| 305 | 311 |
| 306 Object open(callback(value)) { | 312 Object open(callback(value)) { |
| 307 if (_callback != null) throw new StateError('already open'); | 313 if (_callback != null) throw new StateError('already open'); |
| 308 | 314 |
| 309 _callback = callback; | 315 _callback = callback; |
| 310 _observer = observe(_expr, _scope); | 316 _observer = observe(_expr, _scope); |
| 311 _sub = _observer.onUpdate.listen(_check)..onError((e, s) { | 317 _sub = _observer.onUpdate.listen(_convertAndCheck)..onError((e, s) { |
| 312 new Completer().completeError( | 318 new Completer().completeError( |
| 313 "Error evaluating expression '$_observer': $e", s); | 319 "Error evaluating expression '$_observer': $e", s); |
| 314 }); | 320 }); |
| 315 | 321 |
| 322 _check(skipChanges: true); |
| 323 return _value; |
| 324 } |
| 325 |
| 326 bool _check({bool skipChanges: false}) { |
| 316 try { | 327 try { |
| 317 // this causes a call to _updateValue with the new value | 328 // this causes a call to _updateValue with the new value |
| 318 update(_observer, _scope); | 329 update(_observer, _scope); |
| 319 _check(_observer.currentValue, skipChanges: true); | 330 return _convertAndCheck(_observer.currentValue, skipChanges: skipChanges); |
| 320 } catch (e, s) { | 331 } catch (e, s) { |
| 321 new Completer().completeError( | 332 new Completer().completeError( |
| 322 "Error evaluating expression '$_observer': $e", s); | 333 "Error evaluating expression '$_observer': $e", s); |
| 334 return false; |
| 323 } | 335 } |
| 324 return _value; | |
| 325 } | 336 } |
| 326 | 337 |
| 327 void close() { | 338 void close() { |
| 328 if (_callback == null) return; | 339 if (_callback == null) return; |
| 329 | 340 |
| 330 _sub.cancel(); | 341 _sub.cancel(); |
| 331 _sub = null; | 342 _sub = null; |
| 332 _callback = null; | 343 _callback = null; |
| 333 | 344 |
| 334 new Closer().visit(_observer); | 345 new Closer().visit(_observer); |
| 335 _observer = null; | 346 _observer = null; |
| 336 } | 347 } |
| 348 |
| 349 |
| 350 // TODO(jmesserly): the following code is copy+pasted from path_observer.dart |
| 351 // What seems to be going on is: polymer_expressions.dart has its own _Binding |
| 352 // unlike polymer-expressions.js, which builds on CompoundObserver. |
| 353 // This can lead to subtle bugs and should be reconciled. I'm not sure how it |
| 354 // should go, but CompoundObserver does have some nice optimizations around |
| 355 // ObservedSet which are lacking here. And reuse is nice. |
| 356 void deliver() { |
| 357 if (_callback != null) _dirtyCheck(); |
| 358 } |
| 359 |
| 360 bool _dirtyCheck() { |
| 361 var cycles = 0; |
| 362 while (cycles < _MAX_DIRTY_CHECK_CYCLES && _check()) { |
| 363 cycles++; |
| 364 } |
| 365 return cycles > 0; |
| 366 } |
| 367 |
| 368 static const int _MAX_DIRTY_CHECK_CYCLES = 1000; |
| 337 } | 369 } |
| 338 | 370 |
| 339 _identity(x) => x; | 371 _identity(x) => x; |
| 340 | 372 |
| 341 /** | 373 /** |
| 342 * Factory function used for testing. | 374 * Factory function used for testing. |
| 343 */ | 375 */ |
| 344 class ScopeFactory { | 376 class ScopeFactory { |
| 345 const ScopeFactory(); | 377 const ScopeFactory(); |
| 346 modelScope({Object model, Map<String, Object> variables}) => | 378 modelScope({Object model, Map<String, Object> variables}) => |
| 347 new Scope(model: model, variables: variables); | 379 new Scope(model: model, variables: variables); |
| 348 | 380 |
| 349 childScope(Scope parent, String name, Object value) => | 381 childScope(Scope parent, String name, Object value) => |
| 350 parent.childScope(name, value); | 382 parent.childScope(name, value); |
| 351 } | 383 } |
| OLD | NEW |