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

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

Issue 34453003: Rename mdv -> template_binding, remove experiemntal apis from dart:html (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 1 month 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
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of template_binding;
6
7 /** Extensions to [Element]s that behave as templates. */
8 class TemplateBindExtension extends _ElementExtension {
9 var _model;
10 BindingDelegate _bindingDelegate;
11 _TemplateIterator _templateIterator;
12 bool _scheduled = false;
13
14 Element _templateInstanceRef;
15
16 // Note: only used if `this is! TemplateElement`
17 DocumentFragment _content;
18 bool _templateIsDecorated;
19
20 TemplateBindExtension._(Element node) : super(node);
21
22 Element get _node => super._node;
23
24 NodeBinding createBinding(String name, model, String path) {
25 switch (name) {
26 case 'bind':
27 case 'repeat':
28 case 'if':
29 if (_templateIterator == null) {
30 _templateIterator = new _TemplateIterator(_node);
31 }
32 // TODO(jmesserly): why do we do this here and nowhere else?
33 if (path == null) path = '';
34 return new _TemplateBinding(this, name, model, path);
35 default:
36 return super.createBinding(name, model, path);
37 }
38 }
39
40 /**
41 * Creates an instance of the template, using the provided model and optional
42 * binding delegate.
43 */
44 DocumentFragment createInstance([model, BindingDelegate delegate]) {
45 var instance = _createDeepCloneAndDecorateTemplates(
46 templateBind(ref).content, delegate);
47
48 _addBindings(instance, model, delegate);
49 _addTemplateInstanceRecord(instance, model);
50 return instance;
51 }
52
53 /** The data model which is inherited through the tree. */
54 get model => _model;
55
56 void set model(value) {
57 _model = value;
58 _ensureSetModelScheduled();
59 }
60
61 /**
62 * The binding delegate which is inherited through the tree. It can be used
63 * to configure custom syntax for `{{bindings}}` inside this template.
64 */
65 BindingDelegate get bindingDelegate => _bindingDelegate;
66
67 void set bindingDelegate(BindingDelegate value) {
68 _bindingDelegate = value;
69 _ensureSetModelScheduled();
70 }
71
72 _ensureSetModelScheduled() {
73 if (_scheduled) return;
74 _decorate();
75 _scheduled = true;
76 scheduleMicrotask(_setModel);
77 }
78
79 void _setModel() {
80 _scheduled = false;
81 _addBindings(_node, _model, _bindingDelegate);
82 }
83
84 /** Gets the template this node refers to. */
85 Element get ref {
86 _decorate();
87
88 Element result = null;
89 var refId = _node.attributes['ref'];
90 if (refId != null) {
91 var treeScope = _node;
92 while (treeScope.parentNode != null) {
93 treeScope = treeScope.parentNode;
94 }
95
96 // Note: JS code tests that getElementById is present. We can't do that
97 // easily, so instead check for the types known to implement it.
98 if (treeScope is Document ||
99 treeScope is ShadowRoot ||
100 treeScope is SvgSvgElement) {
101
102 result = treeScope.getElementById(refId);
103 }
104 }
105
106 if (result == null) {
107 result = _templateInstanceRef;
108 if (result == null) return _node;
109 }
110
111 var nextRef = templateBind(result).ref;
112 return nextRef != null ? nextRef : result;
113 }
114
115 /**
116 * Gets the content of this template.
117 */
118 DocumentFragment get content {
119 _decorate();
120 return _content != null ? _content : (_node as TemplateElement).content;
121 }
122
123 /**
124 * Ensures proper API and content model for template elements.
125 *
126 * [instanceRef] can be used to set the [Element.ref] property of [template],
127 * and use the ref's content will be used as source when createInstance() is
128 * invoked.
129 *
130 * Returns true if this template was just decorated, or false if it was
131 * already decorated.
132 */
133 static bool decorate(Element template, [Element instanceRef]) =>
134 templateBindFallback(template)._decorate(instanceRef);
135
136 bool _decorate([Element instanceRef]) {
137 // == true check because it starts as a null field.
138 if (_templateIsDecorated == true) return false;
139
140 _injectStylesheet();
141
142 var templateElementExt = this;
143 _templateIsDecorated = true;
144 var isNative = _node is TemplateElement;
145 var bootstrapContents = isNative;
146 var liftContents = !isNative;
147 var liftRoot = false;
148
149 if (!isNative && _isAttributeTemplate(_node)) {
150 if (instanceRef != null) {
151 // TODO(jmesserly): this is just an assert in MDV.
152 throw new ArgumentError('instanceRef should not be supplied for '
153 'attribute templates.');
154 }
155 templateElementExt = templateBind(
156 _extractTemplateFromAttributeTemplate(_node));
157 templateElementExt._templateIsDecorated = true;
158 isNative = templateElementExt._node is TemplateElement;
159 liftRoot = true;
160 }
161
162 if (!isNative) {
163 var doc = _getTemplateContentsOwner(
164 templateElementExt._node.ownerDocument);
165 templateElementExt._content = doc.createDocumentFragment();
166 }
167
168 if (instanceRef != null) {
169 // template is contained within an instance, its direct content must be
170 // empty
171 templateElementExt._templateInstanceRef = instanceRef;
172 } else if (liftContents) {
173 _liftNonNativeChildrenIntoContent(templateElementExt, _node, liftRoot);
174 } else if (bootstrapContents) {
175 bootstrap(templateElementExt.content);
176 }
177
178 return true;
179 }
180
181 static final _contentsOwner = new Expando();
182
183 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html# dfn-template-contents-owner
184 static Document _getTemplateContentsOwner(HtmlDocument doc) {
185 if (doc.window == null) {
186 return doc;
187 }
188 var d = _contentsOwner[doc];
189 if (d == null) {
190 // TODO(arv): This should either be a Document or HTMLDocument depending
191 // on doc.
192 d = doc.implementation.createHtmlDocument('');
193 while (d.lastChild != null) {
194 d.lastChild.remove();
195 }
196 _contentsOwner[doc] = d;
197 }
198 return d;
199 }
200
201 // For non-template browsers, the parser will disallow <template> in certain
202 // locations, so we allow "attribute templates" which combine the template
203 // element with the top-level container node of the content, e.g.
204 //
205 // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
206 //
207 // becomes
208 //
209 // <template repeat="{{ foo }}">
210 // + #document-fragment
211 // + <tr class="bar">
212 // + <td>Bar</td>
213 //
214 static Element _extractTemplateFromAttributeTemplate(Element el) {
215 var template = el.ownerDocument.createElement('template');
216 el.parentNode.insertBefore(template, el);
217
218 for (var name in el.attributes.keys.toList()) {
219 switch (name) {
220 case 'template':
221 el.attributes.remove(name);
222 break;
223 case 'repeat':
224 case 'bind':
225 case 'ref':
226 template.attributes[name] = el.attributes.remove(name);
227 break;
228 }
229 }
230
231 return template;
232 }
233
234 static void _liftNonNativeChildrenIntoContent(TemplateBindExtension template,
235 Element el, bool useRoot) {
236
237 var content = template.content;
238 if (useRoot) {
239 content.append(el);
240 return;
241 }
242
243 var child;
244 while ((child = el.firstChild) != null) {
245 content.append(child);
246 }
247 }
248
249 /**
250 * This used to decorate recursively all templates from a given node.
251 *
252 * By default [decorate] will be called on templates lazily when certain
253 * properties such as [model] are accessed, but it can be run eagerly to
254 * decorate an entire tree recursively.
255 */
256 // TODO(rafaelw): Review whether this is the right public API.
257 static void bootstrap(Node content) {
258 void _bootstrap(template) {
259 if (!TemplateBindExtension.decorate(template)) {
260 bootstrap(templateBind(template).content);
261 }
262 }
263
264 // Need to do this first as the contents may get lifted if |node| is
265 // template.
266 // TODO(jmesserly): content is DocumentFragment or Element
267 var descendents =
268 (content as dynamic).querySelectorAll(_allTemplatesSelectors);
269 if (isSemanticTemplate(content)) {
270 _bootstrap(content);
271 }
272
273 descendents.forEach(_bootstrap);
274 }
275
276 static final String _allTemplatesSelectors =
277 'template, ' +
278 _SEMANTIC_TEMPLATE_TAGS.keys.map((k) => "$k[template]").join(", ");
279
280 static bool _initStyles;
281
282 static void _injectStylesheet() {
283 if (_initStyles == true) return;
284 _initStyles = true;
285
286 var style = new StyleElement();
287 style.text = r'''
288 template,
289 thead[template],
290 tbody[template],
291 tfoot[template],
292 th[template],
293 tr[template],
294 td[template],
295 caption[template],
296 colgroup[template],
297 col[template],
298 option[template] {
299 display: none;
300 }''';
301 document.head.append(style);
302 }
303 }
304
305 class _TemplateBinding extends NodeBinding {
306 TemplateBindExtension _ext;
307
308 // TODO(jmesserly): MDV uses TemplateIterator as the node, see:
309 // https://github.com/Polymer/mdv/issues/127
310 _TemplateBinding(ext, name, model, path)
311 : _ext = ext, super(ext._node, name, model, path) {
312 _ext._templateIterator.inputs.bind(property, model, path);
313 }
314
315 // These are no-ops because we don't use the underlying PathObserver.
316 void _observePath() {}
317 void boundValueChanged(newValue) {}
318
319 void close() {
320 if (closed) return;
321 var templateIterator = _ext._templateIterator;
322 if (templateIterator != null) templateIterator.inputs.unbind(property);
323 super.close();
324 }
325 }
OLDNEW
« no previous file with comments | « pkg/template_binding/lib/src/select_element.dart ('k') | pkg/template_binding/lib/src/template_iterator.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698