| Index: lib/templating.dart
|
| diff --git a/lib/templating.dart b/lib/templating.dart
|
| index d6541fa4d4d0a3324b0b783cdc8dab3970be3163..8eb10382b9940da436ba1da96574c0d0a0ac9dc9 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,24 @@ 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
|
| + ? 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 +278,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 +396,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;
|
|
|