Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(976)

Side by Side Diff: pkg/template_binding/lib/src/node.dart

Issue 355133002: switch Node.bind to interop (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 template_binding; 5 part of template_binding;
6 6
7 /** Extensions to the [Node] API. */ 7 /** Extensions to the [Node] API. */
8 class NodeBindExtension { 8 class NodeBindExtension {
9 final Node _node; 9 final Node _node;
10 final JsObject _js;
11
12 NodeBindExtension._(node)
13 : _node = node,
14 _js = new JsObject.fromBrowserObject(node);
10 15
11 /** 16 /**
12 * Gets the data bindings that are associated with this node, if any. 17 * Gets the data bindings that are associated with this node, if any.
13 * 18 *
14 * This starts out null, and if [enableBindingsReflection] is enabled, calls 19 * This starts out null, and if [enableBindingsReflection] is enabled, calls
15 * to [bind] will initialize this field and the binding. 20 * to [bind] will initialize this field and the binding.
16 */ 21 */
17 // Dart note: in JS this has a trailing underscore, meaning "private". 22 // Dart note: in JS this has a trailing underscore, meaning "private".
18 // But in dart if we made it _bindings, it wouldn't be accessible at all. 23 // But in dart if we made it _bindings, it wouldn't be accessible at all.
19 // It is unfortunately needed to implement Node.bind correctly. 24 // It is unfortunately needed to implement Node.bind correctly.
20 Map<String, Bindable> bindings; 25 Map<String, Bindable> get bindings {
21 26 var b = _js['bindings_'];
22 NodeBindExtension._(this._node); 27 if (b == null) return null;
28 // TODO(jmesserly): should cache this for identity.
29 return new _NodeBindingsMap(b);
30 }
31
32 set bindings(Map<String, Bindable> value) {
33 if (value == null) {
34 _js.deleteProperty('bindings_');
35 return;
36 }
37 var b = bindings;
38 if (b == null) {
39 _js['bindings_'] = new JsObject.jsify({});
40 b = bindings;
41 }
42 b.addAll(value);
43 }
23 44
24 /** 45 /**
25 * Binds the attribute [name] to [value]. [value] can be a simple value when 46 * Binds the attribute [name] to [value]. [value] can be a simple value when
26 * [oneTime] is true, or a [Bindable] like [PathObserver]. 47 * [oneTime] is true, or a [Bindable] like [PathObserver].
27 * Returns the [Bindable] instance. 48 * Returns the [Bindable] instance.
28 */ 49 */
29 Bindable bind(String name, value, {bool oneTime: false}) { 50 Bindable bind(String name, value, {bool oneTime: false}) {
30 // TODO(jmesserly): in Dart we could deliver an async error, which would 51 name = _dartToJsName(name);
31 // have a similar affect but be reported as a test failure. Should we? 52
32 window.console.error('Unhandled binding to Node: ' 53 if (!oneTime && value is Bindable) {
33 '$this $name $value $oneTime'); 54 value = bindableToJsObject(value);
34 return null; 55 }
56 return jsObjectToBindable(_js.callMethod('bind', [name, value, oneTime]));
35 } 57 }
36 58
37 /** 59 /**
38 * Called when all [bind] calls are finished for a given template expansion. 60 * Called when all [bind] calls are finished for a given template expansion.
39 */ 61 */
40 void bindFinished() {} 62 bindFinished() => _js.callMethod('bindFinished');
41 63
42 /** 64 // Note: confusingly this is on NodeBindExtension because it can be on any
43 * Dispatch support so custom HtmlElement's can override these methods. 65 // Node. It's really an API added by TemplateBinding. Therefore it stays
44 * 66 // implemented in Dart because TemplateBinding still is.
45 * A public method like [this.bind] should not call another public method.
46 *
47 * Instead it should dispatch through [_self] to give the "overridden" method
48 * a chance to intercept.
49 */
50 NodeBindExtension get _self => _node is NodeBindExtension ? _node : this;
51
52 TemplateInstance _templateInstance; 67 TemplateInstance _templateInstance;
53 68
54 /** Gets the template instance that instantiated this node, if any. */ 69 /** Gets the template instance that instantiated this node, if any. */
55 TemplateInstance get templateInstance => 70 TemplateInstance get templateInstance =>
56 _templateInstance != null ? _templateInstance : 71 _templateInstance != null ? _templateInstance :
57 (_node.parent != null ? nodeBind(_node.parent).templateInstance : null); 72 (_node.parent != null ? nodeBind(_node.parent).templateInstance : null);
73 }
58 74
59 _open(Bindable bindable, callback(value)) => 75 class _NodeBindingsMap extends MapBase<String, Bindable> {
60 callback(bindable.open(callback)); 76 final JsObject _bindings;
61 77
62 Bindable _maybeUpdateBindings(String name, Bindable binding) { 78 _NodeBindingsMap(this._bindings);
63 return enableBindingsReflection ? _updateBindings(name, binding) : binding; 79
80 // TODO(jmesserly): this should be lazy
81 Iterable<String> get keys =>
82 js.context['Object'].callMethod('keys', [_bindings]).map(_jsToDartName);
83
84 Bindable operator[](String name) =>
85 jsObjectToBindable(_bindings[_dartToJsName(name)]);
86
87 operator[]=(String name, Bindable value) {
88 _bindings[_dartToJsName(name)] = bindableToJsObject(value);
64 } 89 }
65 90
66 Bindable _updateBindings(String name, Bindable binding) { 91 @override Bindable remove(String name) {
67 if (bindings == null) bindings = {}; 92 name = _dartToJsName(name);
68 var old = bindings[name]; 93 var old = this[name];
69 if (old != null) old.close(); 94 _bindings.deleteProperty(name);
70 return bindings[name] = binding; 95 return old;
96 }
97
98 @override void clear() {
99 // Notes: this implementation only works because our "keys" returns a copy.
100 // We could also make it O(1) by assigning a new JS object to the bindings_
101 // property, if performance is an issue.
102 keys.forEach(remove);
71 } 103 }
72 } 104 }
73 105
106 // TODO(jmesserly): perhaps we should switch Dart's Node.bind API back to
107 // 'textContent' for consistency? This only affects the raw Node.bind API when
108 // called on Text nodes, which is unlikely to be used except by TemplateBinding.
109 // Seems like a lot of magic to support it. I don't think Node.bind promises any
110 // strong relationship between properties and [name], so textContent seems fine.
111 String _dartToJsName(String name) {
112 if (name == 'text') name = 'textContent';
113 return name;
114 }
115
116
117 String _jsToDartName(String name) {
118 if (name == 'textContent') name = 'text';
119 return name;
120 }
121
122
123 /// Given a bindable [JsObject], wraps it in a Dart [Bindable].
124 /// See [bindableToJsObject] to go in the other direction.
125 Bindable jsObjectToBindable(JsObject obj) {
126 if (obj == null) return null;
127 var b = obj['__dartBindable'];
128 // For performance, unwrap the Dart bindable if we find one.
129 // Note: in the unlikely event some code messes with our __dartBindable
130 // property we can simply fallback to a _JsBindable wrapper.
131 return b is Bindable ? b : new _JsBindable(obj);
132 }
133
134 class _JsBindable extends Bindable {
135 final JsObject _js;
136 _JsBindable(JsObject obj) : _js = obj;
137
138 open(callback) => _js.callMethod('open', [callback]);
139
140 close() => _js.callMethod('close');
141
142 get value => _js.callMethod('discardChanges');
143
144 set value(newValue) {
145 _js.callMethod('setValue', [newValue]);
146 }
147
148 deliver() => _js.callMethod('deliver');
149 }
150
151 /// Given a [bindable], create a JS object proxy for it.
152 /// This is the inverse of [jsObjectToBindable].
153 JsObject bindableToJsObject(Bindable bindable) {
154 if (bindable is _JsBindable) return bindable._js;
155
156 return new JsObject.jsify({
157 'open': (callback) => bindable.open((x) => callback.apply([x])),
158 'close': () => bindable.close(),
159 'discardChanges': () => bindable.value,
160 'setValue': (x) => bindable.value = x,
161 // NOTE: this is not used by Node.bind, but it's used by Polymer:
162 // https://github.com/Polymer/polymer-dev/blob/ba2b68fe5a5721f60b5994135f327 0e63588809a/src/declaration/properties.js#L130
163 // Technically this works because 'deliver' is on PathObserver and
164 // CompoundObserver. But ideally Polymer-JS would not assume that.
165 'deliver': () => bindable.deliver(),
166 // Save this so we can return it from [jsObjectToBindable]
167 '__dartBindable': bindable
168 });
169 }
74 170
75 /** Information about the instantiated template. */ 171 /** Information about the instantiated template. */
76 class TemplateInstance { 172 class TemplateInstance {
77 // TODO(rafaelw): firstNode & lastNode should be read-synchronous 173 // TODO(rafaelw): firstNode & lastNode should be read-synchronous
78 // in cases where script has modified the template instance boundary. 174 // in cases where script has modified the template instance boundary.
79 175
80 /** The first node of this template instantiation. */ 176 /** The first node of this template instantiation. */
81 Node get firstNode => _firstNode; 177 Node get firstNode => _firstNode;
82 178
83 /** 179 /**
84 * The last node of this template instantiation. 180 * The last node of this template instantiation.
85 * This could be identical to [firstNode] if the template only expanded to a 181 * This could be identical to [firstNode] if the template only expanded to a
86 * single node. 182 * single node.
87 */ 183 */
88 Node get lastNode => _lastNode; 184 Node get lastNode => _lastNode;
89 185
90 /** The model used to instantiate the template. */ 186 /** The model used to instantiate the template. */
91 final model; 187 final model;
92 188
93 Node _firstNode, _lastNode; 189 Node _firstNode, _lastNode;
94 190
95 TemplateInstance(this.model); 191 TemplateInstance(this.model);
96 } 192 }
OLDNEW
« no previous file with comments | « pkg/template_binding/lib/src/input_element.dart ('k') | pkg/template_binding/lib/src/select_element.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698