Chromium Code Reviews| Index: lib/templating.dart |
| diff --git a/lib/templating.dart b/lib/templating.dart |
| index d6541fa4d4d0a3324b0b783cdc8dab3970be3163..b8d84ce7dd3652eae51772e4562da3fbf4f6c248 100644 |
| --- a/lib/templating.dart |
| +++ b/lib/templating.dart |
| @@ -147,7 +147,8 @@ void changeCssClasses(elem, ChangeRecord change) { |
| * bindCssClasses(e, () => class1); |
| * bindCssClasses(e, () => class2); |
| */ |
| -ChangeUnobserver bindCssClasses(Element elem, dynamic exp()) { |
| +ChangeUnobserver bindCssClasses(Element elem, dynamic exp(), |
| + [String debugLocation]) { |
| return watchAndInvoke(exp, (e) { |
| if (e.changes != null) { |
| for (var change in e.changes) changeCssClasses(elem, change); |
| @@ -155,13 +156,14 @@ ChangeUnobserver bindCssClasses(Element elem, dynamic exp()) { |
| updateCssClass(elem, false, e.oldValue); |
| updateCssClass(elem, true, e.newValue); |
| } |
| - }, 'css-class-bind'); |
| + }, 'css-class-bind', debugLocation); |
| } |
| /** Bind the result of [exp] to the style attribute in [elem]. */ |
| -ChangeUnobserver bindStyle(Element elem, Map<String, String> exp()) { |
| +ChangeUnobserver bindStyle(Element elem, Map<String, String> exp(), |
| + [String debugLocation]) { |
| return watchAndInvoke(exp, (e) => updateStyle(elem, e.oldValue, e.newValue), |
| - 'css-style-bind'); |
| + 'css-style-bind', debugLocation); |
| } |
| /** |
| @@ -249,22 +251,23 @@ class Listener extends TemplateItem { |
| } |
| } |
| -/** Represents a generic data binding and a corresponding action. */ |
| -class Binding extends TemplateItem { |
| +/** Common logic for all bindings. */ |
| +abstract class Binding extends TemplateItem { |
| final exp; |
| - final ChangeObserver action; |
| final bool isFinal; |
| + final String debugLocation; |
| ChangeUnobserver stopper; |
| - Binding(this.exp, this.action, this.isFinal); |
| + Binding(this.exp, this.isFinal) |
| + : debugLocation = verboseDebugMessages ? _readCurrentStackTrace() : null; |
| void insert() { |
| if (isFinal) { |
| - action(new ChangeNotification(null, exp())); |
| + invokeCallback(); |
| } else if (stopper != null) { |
| throw new StateError('binding already attached'); |
| } else { |
| - stopper = watchAndInvoke(exp, action, 'generic-binding'); |
| + stopper = registerAndInvoke(); |
| } |
| } |
| @@ -274,109 +277,81 @@ class Binding extends TemplateItem { |
| stopper = null; |
| } |
| } |
| + |
| + /** Invokes the action associated with this binding. */ |
| + void invokeCallback(); |
| + |
| + /** |
| + * Registers the watcher and invokes the action associated with this binding. |
| + */ |
| + ChangeUnobserver registerAndInvoke(); |
| +} |
| + |
| +/** Represents a generic data binding and a corresponding action. */ |
| +class GenericBinding extends Binding { |
| + final ChangeObserver action; |
| + |
| + GenericBinding(exp, this.action, bool isFinal) : super(exp, isFinal); |
| + |
| + void invokeCallback() => action(new ChangeNotification(null, exp())); |
| + |
| + ChangeUnobserver registerAndInvoke() => |
| + watchAndInvoke(exp, action, 'generic-binding', debugLocation); |
| } |
| /** Represents a binding to a style attribute. */ |
| -class StyleAttrBinding extends TemplateItem { |
| - final exp; |
| +class StyleAttrBinding extends Binding { |
| final Element elem; |
| - final bool isFinal; |
| - ChangeUnobserver stopper; |
| - StyleAttrBinding(this.elem, this.exp, this.isFinal); |
| + StyleAttrBinding(this.elem, exp, bool isFinal) : super(exp, isFinal); |
| - void insert() { |
| - if (isFinal) { |
| - updateStyle(elem, null, exp()); |
| - } else if (stopper != null) { |
| - throw new StateError('style binding already attached'); |
| - } else { |
| - stopper = bindStyle(elem, exp); |
| - } |
| - } |
| + void invokeCallback() => updateStyle(elem, null, exp()); |
| - void remove() { |
| - if (!isFinal) { |
| - stopper(); |
| - stopper = null; |
| - } |
| - } |
| + ChangeUnobserver registerAndInvoke() => bindStyle(elem, exp, debugLocation); |
| } |
| /** Represents a binding to a class attribute. */ |
| -class ClassAttrBinding extends TemplateItem { |
| +class ClassAttrBinding extends Binding { |
| final Element elem; |
| - final exp; |
| - final bool isFinal; |
| - ChangeUnobserver stopper; |
| - ClassAttrBinding(this.elem, this.exp, this.isFinal); |
| + ClassAttrBinding(this.elem, exp, bool isFinal) : super(exp, isFinal); |
| - void insert() { |
| - if (isFinal) { |
| - updateCssClass(elem, true, exp()); |
| - } else if (stopper != null) { |
| - throw new StateError('class binding already attached'); |
| - } else { |
| - stopper = bindCssClasses(elem, exp); |
| - } |
| - } |
| + void invokeCallback() => updateCssClass(elem, true, exp()); |
| - void remove() { |
| - if (!isFinal) { |
| - stopper(); |
| - stopper = null; |
| - } |
| - } |
| + ChangeUnobserver registerAndInvoke() => |
| + bindCssClasses(elem, exp, debugLocation); |
| } |
| /** |
| * Represents a one-way binding between a dart getter expression and a DOM |
| * property, or conversely between a DOM property value and a dart property. |
| */ |
| -class DomPropertyBinding extends TemplateItem { |
| +class DomPropertyBinding extends Binding { |
| /** Value updated by this binding. */ |
| final Setter setter; |
| /** |
| - * Getter that reads the value of the binding, either from a Dart expression |
| - * or from a DOM property (which is internally also a Dart expression). |
| - */ |
| - final Getter getter; |
| - |
| - /** |
| * Whether this is a binding that assigns a DOM attribute accepting URL |
| * values. If so, the value assigned to the attribute needs to be sanitized. |
| */ |
| final bool isUrl; |
| - final bool isFinal; |
| - |
| - ChangeUnobserver stopper; |
| - |
| - DomPropertyBinding(this.getter, this.setter, this.isUrl, this.isFinal); |
| + /** |
| + * Creates a DOM property binding, where [getter] reads the value of the |
| + * binding, either from a Dart expression or from a DOM property (which is |
| + * internally also a Dart expression). |
| + */ |
| + DomPropertyBinding(Getter getter, this.setter, this.isUrl, bool isFinal) |
| + : super(getter, isFinal); |
| void _safeSetter(value) { |
| setter(isUrl ? sanitizeUri(value) : value); |
| } |
| + void invokeCallback() => _safeSetter(exp()); |
| - void insert() { |
| - if (isFinal) { |
| - _safeSetter(getter()); |
| - } else if (stopper != null) { |
| - throw new StateError('data binding already attached.'); |
| - } else { |
| - stopper = watchAndInvoke(getter, (e) => _safeSetter(e.newValue), |
| - 'dom-property-binding'); |
| - } |
| - } |
| - |
| - void remove() { |
| - if (!isFinal) { |
| - stopper(); |
| - stopper = null; |
| - } |
| - } |
| + ChangeUnobserver registerAndInvoke() => |
| + watchAndInvoke(exp, (e) => _safeSetter(e.newValue), |
| + 'dom-property-binding', debugLocation); |
| } |
| /** Represents a component added within a template. */ |
| @@ -420,13 +395,13 @@ class Template extends TemplateItem { |
| /** Run [action] when [exp] changes (while this template is visible). */ |
| void bind(exp, ChangeObserver action, bool isFinal) { |
| - children.add(new Binding(exp, action, isFinal)); |
| + children.add(new GenericBinding(exp, action, isFinal)); |
| } |
| /** Create and bind a [Node] to [exp] while this template is visible. */ |
| Node contentBind(Function exp, isFinal) { |
| var bindNode = new Text(''); |
| - children.add(new Binding(() => '${exp()}', (e) { |
| + children.add(new GenericBinding(() => '${exp()}', (e) { |
| bindNode = updateBinding(exp(), bindNode, e.newValue); |
| }, isFinal)); |
| return bindNode; |
| @@ -676,3 +651,12 @@ class LoopTemplateInAttribute extends Template { |
| stopper = null; |
| } |
| } |
| + |
| +String _readCurrentStackTrace() { |
| + try { |
| + throw ""; |
| + } catch (e, trace) { |
| + return trace.toString(); |
|
Jennifer Messerly
2013/07/11 21:27:23
use http://pub.dartlang.org/packages/stack_trace ?
Siggi Cherem (dart-lang)
2013/07/11 22:07:01
Oh, it used to depend on dart:io, but seems to hav
|
| + } |
| +} |
| + |