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

Side by Side Diff: pkg/mdv/lib/src/template_iterator.dart

Issue 20062002: [mdv] Simplified Mustache Parsing (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 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 13 matching lines...) Expand all
24 } 24 }
25 } 25 }
26 26
27 for (var c = node.firstChild; c != null; c = c.nextNode) { 27 for (var c = node.firstChild; c != null; c = c.nextNode) {
28 clone.append(_createDeepCloneAndDecorateTemplates(c, delegate)); 28 clone.append(_createDeepCloneAndDecorateTemplates(c, delegate));
29 } 29 }
30 return clone; 30 return clone;
31 } 31 }
32 32
33 void _addBindings(Node node, model, [BindingDelegate delegate]) { 33 void _addBindings(Node node, model, [BindingDelegate delegate]) {
34 List bindings = null;
34 if (node is Element) { 35 if (node is Element) {
35 _addAttributeBindings(node, model, delegate); 36 bindings = _parseAttributeBindings(node);
36 } else if (node is Text) { 37 } else if (node is Text) {
37 _parseAndBind(node, 'text', node.text, model, delegate); 38 var tokens = _parseMustacheTokens(node.text);
39 if (tokens != null) bindings = ['text', tokens];
40 }
41
42 if (bindings != null) {
43 _processBindings(bindings, node, model, delegate);
38 } 44 }
39 45
40 for (var c = node.firstChild; c != null; c = c.nextNode) { 46 for (var c = node.firstChild; c != null; c = c.nextNode) {
41 _addBindings(c, model, delegate); 47 _addBindings(c, model, delegate);
42 } 48 }
43 } 49 }
44 50
45 void _addAttributeBindings(Element element, model, delegate) { 51 List _parseAttributeBindings(Element element) {
46 var attrs = new LinkedHashMap.from(element.attributes); 52 var bindings = null;
47 if (element.isTemplate) { 53 var ifFound = false;
48 // Accept 'naked' bind & repeat. 54 var bindFound = false;
49 if (attrs['bind'] == '') attrs['bind'] = '{{}}'; 55 var isTemplateNode = element.isTemplate;
50 if (attrs['repeat'] == '') attrs['repeat'] = '{{}}';
51 56
52 // Treat <template if="{{expr}}"> as <template bind if="{{expr}}"> 57 element.attributes.forEach((name, value) {
53 if (attrs.containsKey('if') && 58 if (isTemplateNode) {
54 !attrs.containsKey('bind') && 59 if (name == 'if') {
55 !attrs.containsKey('repeat')) { 60 ifFound = true;
56 attrs['bind'] = '{{}}'; 61 } else if (name == 'bind' || name == 'repeat') {
62 bindFound = true;
63 if (value == '') value = '{{}}';
64 }
57 } 65 }
66
67 var tokens = _parseMustacheTokens(value);
68 if (tokens != null) {
69 if (bindings == null) bindings = [];
70 bindings..add(name)..add(tokens);
71 }
72 });
73
74 // Treat <template if> as <template bind if>
75 if (ifFound && !bindFound) {
76 if (bindings == null) bindings = [];
77 bindings..add('bind')..add(_parseMustacheTokens('{{}}'));
58 } 78 }
59 79
60 attrs.forEach((name, value) { 80 return bindings;
61 _parseAndBind(element, name, value, model, delegate);
62 });
63 } 81 }
64 82
65 void _parseAndBind(Node node, String name, String text, model, 83 void _processBindings(List bindings, Node node, model,
66 BindingDelegate delegate) { 84 BindingDelegate delegate) {
67 85
68 var tokens = _parseMustacheTokens(text); 86 for (var i = 0; i < bindings.length; i += 2) {
69 if (tokens.length == 0 || (tokens.length == 1 && tokens[0].isText)) { 87 _setupBinding(node, bindings[i], bindings[i + 1], model, delegate);
70 return;
71 } 88 }
89 }
90
91 void _setupBinding(Node node, String name, List tokens, model,
92 BindingDelegate delegate) {
72 93
73 // If this is a custom element, give the .xtag a change to bind. 94 // If this is a custom element, give the .xtag a change to bind.
74 node = _nodeOrCustom(node); 95 node = _nodeOrCustom(node);
75 96
76 if (tokens.length == 1 && tokens[0].isBinding) { 97 if (_isSimpleBinding(tokens)) {
77 _bindOrDelegate(node, name, model, tokens[0].value, delegate); 98 _bindOrDelegate(node, name, model, tokens[1], delegate);
78 return; 99 return;
79 } 100 }
80 101
81 var replacementBinding = new CompoundBinding(); 102 var replacementBinding = new CompoundBinding();
82 for (var i = 0; i < tokens.length; i++) { 103 for (var i = 1; i < tokens.length; i += 2) {
83 var token = tokens[i]; 104 // TODO(jmesserly): not sure if this index is correct. See my comment here:
84 if (token.isBinding) { 105 // https://github.com/Polymer/mdv/commit/f1af6fe683fd06eed2a7a7849f01c227db1 2cda3#L0L1035
85 _bindOrDelegate(replacementBinding, i, model, token.value, delegate); 106 _bindOrDelegate(replacementBinding, i, model, tokens[i], delegate);
86 }
87 } 107 }
88 108
89 replacementBinding.combinator = (values) { 109 replacementBinding.combinator = (values) {
90 var newValue = new StringBuffer(); 110 var newValue = new StringBuffer();
91 111
92 for (var i = 0; i < tokens.length; i++) { 112 for (var i = 0, text = true; i < tokens.length; i++, text = !text) {
93 var token = tokens[i]; 113 if (text) {
94 if (token.isText) { 114 newValue.write(tokens[i]);
95 newValue.write(token.value);
96 } else { 115 } else {
97 var value = values[i]; 116 var value = values[i];
98 if (value != null) { 117 if (value != null) {
99 newValue.write(value); 118 newValue.write(value);
100 } 119 }
101 } 120 }
102 } 121 }
103 122
104 return newValue.toString(); 123 return newValue.toString();
105 }; 124 };
(...skipping 16 matching lines...) Expand all
122 } 141 }
123 142
124 /** 143 /**
125 * Gets the [node]'s custom [Element.xtag] if present, otherwise returns 144 * Gets the [node]'s custom [Element.xtag] if present, otherwise returns
126 * the node. This is used so nodes can override [Node.bind], [Node.unbind], 145 * the node. This is used so nodes can override [Node.bind], [Node.unbind],
127 * and [Node.unbindAll] like InputElement does. 146 * and [Node.unbindAll] like InputElement does.
128 */ 147 */
129 // TODO(jmesserly): remove this when we can extend Element for real. 148 // TODO(jmesserly): remove this when we can extend Element for real.
130 _nodeOrCustom(node) => node is Element ? node.xtag : node; 149 _nodeOrCustom(node) => node is Element ? node.xtag : node;
131 150
132 List<_BindingToken> _parseMustacheTokens(String s) { 151 /** True if and only if [tokens] is of the form `['', path, '']`. */
133 var result = []; 152 bool _isSimpleBinding(List<String> tokens) =>
153 tokens.length == 3 && tokens[0].isEmpty && tokens[2].isEmpty;
154
155 /**
156 * Parses {{ mustache }} bindings.
157 *
158 * Returns null if there are no matches. Otherwise returns
159 * [TEXT, (PATH, TEXT)+] if there is at least one mustache.
160 */
161 List<String> _parseMustacheTokens(String s) {
162 if (s.isEmpty) return;
163
164 var tokens = null;
134 var length = s.length; 165 var length = s.length;
135 var index = 0, lastIndex = 0; 166 var startIndex = 0, lastIndex = 0, endIndex = 0;
136 while (lastIndex < length) { 167 while (lastIndex < length) {
137 index = s.indexOf('{{', lastIndex); 168 startIndex = s.indexOf('{{', lastIndex);
138 if (index < 0) { 169 endIndex = startIndex < 0 ? -1 : s.indexOf('}}', startIndex + 2);
139 result.add(new _BindingToken(s.substring(lastIndex))); 170
171 if (endIndex < 0) {
172 if (tokens == null) return null;
173
174 tokens.add(s.substring(lastIndex));
140 break; 175 break;
141 } else { 176 }
142 // There is a non-empty text run before the next path token.
143 if (index > 0 && lastIndex < index) {
144 result.add(new _BindingToken(s.substring(lastIndex, index)));
145 }
146 lastIndex = index + 2;
147 index = s.indexOf('}}', lastIndex);
148 if (index < 0) {
149 var text = s.substring(lastIndex - 2);
150 if (result.length > 0 && result.last.isText) {
151 result.last.value += text;
152 } else {
153 result.add(new _BindingToken(text));
154 }
155 break;
156 }
157 177
158 var value = s.substring(lastIndex, index).trim(); 178 if (tokens == null) tokens = <String>[];
159 result.add(new _BindingToken(value, isBinding: true)); 179 tokens.add(s.substring(lastIndex, startIndex)); // TEXT
160 lastIndex = index + 2; 180 tokens.add(s.substring(startIndex + 2, endIndex).trim()); // PATH
161 } 181 lastIndex = endIndex + 2;
162 } 182 }
163 return result; 183
184 if (lastIndex == length) tokens.add('');
185 return tokens;
164 } 186 }
165 187
166 void _addTemplateInstanceRecord(fragment, model) { 188 void _addTemplateInstanceRecord(fragment, model) {
167 if (fragment.firstChild == null) { 189 if (fragment.firstChild == null) {
168 return; 190 return;
169 } 191 }
170 192
171 var instanceRecord = new TemplateInstance( 193 var instanceRecord = new TemplateInstance(
172 fragment.firstChild, fragment.lastChild, model); 194 fragment.firstChild, fragment.lastChild, model);
173 195
174 var node = instanceRecord.firstNode; 196 var node = instanceRecord.firstNode;
175 while (node != null) { 197 while (node != null) {
176 _mdv(node)._templateInstance = instanceRecord; 198 _mdv(node)._templateInstance = instanceRecord;
177 node = node.nextNode; 199 node = node.nextNode;
178 } 200 }
179 } 201 }
180 202
181 class _BindingToken {
182 final String value;
183 final bool isBinding;
184
185 _BindingToken(this.value, {this.isBinding: false});
186
187 bool get isText => !isBinding;
188 }
189 203
190 class _TemplateIterator { 204 class _TemplateIterator {
191 final Element _templateElement; 205 final Element _templateElement;
192 final List<Node> terminators = []; 206 final List<Node> terminators = [];
193 final CompoundBinding inputs; 207 final CompoundBinding inputs;
194 List iteratedValue; 208 List iteratedValue;
195 Object _lastValue; 209 Object _lastValue;
196 210
197 StreamSubscription _sub; 211 StreamSubscription _sub;
198 StreamSubscription _valueBinding; 212 StreamSubscription _valueBinding;
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after
386 nodeExt._templateIterator = null; 400 nodeExt._templateIterator = null;
387 } 401 }
388 } 402 }
389 403
390 _nodeOrCustom(node).unbindAll(); 404 _nodeOrCustom(node).unbindAll();
391 for (var c = node.firstChild; c != null; c = c.nextNode) { 405 for (var c = node.firstChild; c != null; c = c.nextNode) {
392 _unbindAllRecursively(c); 406 _unbindAllRecursively(c);
393 } 407 }
394 } 408 }
395 } 409 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698