| OLD | NEW |
| 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 [Element]s that behave as templates. */ | 7 /** Extensions to [Element]s that behave as templates. */ |
| 8 class TemplateBindExtension extends _ElementExtension { | 8 class TemplateBindExtension extends _ElementExtension { |
| 9 var _model; | 9 var _model; |
| 10 BindingDelegate _bindingDelegate; | 10 BindingDelegate _bindingDelegate; |
| 11 _TemplateIterator _iterator; | 11 _TemplateIterator _iterator; |
| 12 bool _scheduled = false; | 12 bool _scheduled = false; |
| 13 | 13 |
| 14 Element _templateInstanceRef; | 14 Element _templateInstanceRef; |
| 15 | 15 |
| 16 // Note: only used if `this is! TemplateElement` | 16 // Note: only used if `this is! TemplateElement` |
| 17 DocumentFragment _content; | 17 DocumentFragment _content; |
| 18 bool _templateIsDecorated; | 18 bool _templateIsDecorated; |
| 19 | 19 |
| 20 HtmlDocument _stagingDocument; | |
| 21 | |
| 22 var _bindingMap; | 20 var _bindingMap; |
| 23 | 21 |
| 24 TemplateBindExtension._(Element node) : super(node); | 22 TemplateBindExtension._(Element node) : super(node); |
| 25 | 23 |
| 26 Element get _node => super._node; | 24 Element get _node => super._node; |
| 27 | 25 |
| 28 TemplateBindExtension get _self => super._node is TemplateBindExtension | 26 TemplateBindExtension get _self => super._node is TemplateBindExtension |
| 29 ? _node : this; | 27 ? _node : this; |
| 30 | 28 |
| 31 NodeBinding bind(String name, model, [String path]) { | 29 NodeBinding bind(String name, model, [String path]) { |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 113 // Dart note: we store _bindingMap on the TemplateBindExtension instead of | 111 // Dart note: we store _bindingMap on the TemplateBindExtension instead of |
| 114 // the "content" because we already have an expando for it. | 112 // the "content" because we already have an expando for it. |
| 115 var map = ref._bindingMap; | 113 var map = ref._bindingMap; |
| 116 if (map == null) { | 114 if (map == null) { |
| 117 // TODO(rafaelw): Setup a MutationObserver on content to detect | 115 // TODO(rafaelw): Setup a MutationObserver on content to detect |
| 118 // when the instanceMap is invalid. | 116 // when the instanceMap is invalid. |
| 119 map = new _InstanceBindingMap(content, delegate); | 117 map = new _InstanceBindingMap(content, delegate); |
| 120 ref._bindingMap = map; | 118 ref._bindingMap = map; |
| 121 } | 119 } |
| 122 | 120 |
| 123 var staging = _getTemplateStagingDocument(); | 121 var instance = map.hasSubTemplate |
| 124 var instance = _deepCloneIgnoreTemplateContent(content, staging); | 122 ? _deepCloneIgnoreTemplateContent(content) |
| 123 : content.clone(true); |
| 125 | 124 |
| 126 _addMapBindings(instance, map, model, delegate, bound); | 125 _addMapBindings(instance, map, model, delegate, bound); |
| 127 // TODO(rafaelw): We can do this more lazily, but setting a sentinel | 126 // TODO(rafaelw): We can do this more lazily, but setting a sentinel |
| 128 // in the parent of the template element, and creating it when it's | 127 // in the parent of the template element, and creating it when it's |
| 129 // asked for by walking back to find the iterating template. | 128 // asked for by walking back to find the iterating template. |
| 130 _addTemplateInstanceRecord(instance, model); | 129 _addTemplateInstanceRecord(instance, model); |
| 131 return instance; | 130 return instance; |
| 132 } | 131 } |
| 133 | 132 |
| 134 /** The data model which is inherited through the tree. */ | 133 /** The data model which is inherited through the tree. */ |
| 135 get model => _model; | 134 get model => _model; |
| 136 | 135 |
| 137 void set model(value) { | 136 void set model(value) { |
| 138 _model = value; | 137 _model = value; |
| 139 _ensureSetModelScheduled(); | 138 _ensureSetModelScheduled(); |
| 140 } | 139 } |
| 141 | 140 |
| 142 static Node _deepCloneIgnoreTemplateContent(Node node, stagingDocument) { | 141 static Node _deepCloneIgnoreTemplateContent(Node node) { |
| 143 var clone = stagingDocument.importNode(node, false); | 142 var clone = node.clone(false); // Shallow clone. |
| 144 if (isSemanticTemplate(clone)) return clone; | 143 if (isSemanticTemplate(clone)) return clone; |
| 145 | 144 |
| 146 for (var c = node.firstChild; c != null; c = c.nextNode) { | 145 for (var c = node.firstChild; c != null; c = c.nextNode) { |
| 147 clone.append(_deepCloneIgnoreTemplateContent(c, stagingDocument)); | 146 clone.append(_deepCloneIgnoreTemplateContent(c)); |
| 148 } | 147 } |
| 149 return clone; | 148 return clone; |
| 150 } | 149 } |
| 151 | 150 |
| 152 /** | 151 /** |
| 153 * The binding delegate which is inherited through the tree. It can be used | 152 * The binding delegate which is inherited through the tree. It can be used |
| 154 * to configure custom syntax for `{{bindings}}` inside this template. | 153 * to configure custom syntax for `{{bindings}}` inside this template. |
| 155 */ | 154 */ |
| 156 BindingDelegate get bindingDelegate => _bindingDelegate; | 155 BindingDelegate get bindingDelegate => _bindingDelegate; |
| 157 | 156 |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 'attribute templates.'); | 234 'attribute templates.'); |
| 236 } | 235 } |
| 237 templateElementExt = templateBind( | 236 templateElementExt = templateBind( |
| 238 _extractTemplateFromAttributeTemplate(_node)); | 237 _extractTemplateFromAttributeTemplate(_node)); |
| 239 templateElementExt._templateIsDecorated = true; | 238 templateElementExt._templateIsDecorated = true; |
| 240 isNative = templateElementExt._node is TemplateElement; | 239 isNative = templateElementExt._node is TemplateElement; |
| 241 liftRoot = true; | 240 liftRoot = true; |
| 242 } | 241 } |
| 243 | 242 |
| 244 if (!isNative) { | 243 if (!isNative) { |
| 245 var doc = _getOrCreateTemplateContentsOwner(templateElementExt._node); | 244 var doc = _getTemplateContentsOwner( |
| 245 templateElementExt._node.ownerDocument); |
| 246 templateElementExt._content = doc.createDocumentFragment(); | 246 templateElementExt._content = doc.createDocumentFragment(); |
| 247 } | 247 } |
| 248 | 248 |
| 249 if (instanceRef != null) { | 249 if (instanceRef != null) { |
| 250 // template is contained within an instance, its direct content must be | 250 // template is contained within an instance, its direct content must be |
| 251 // empty | 251 // empty |
| 252 templateElementExt._templateInstanceRef = instanceRef; | 252 templateElementExt._templateInstanceRef = instanceRef; |
| 253 } else if (liftContents) { | 253 } else if (liftContents) { |
| 254 _liftNonNativeChildrenIntoContent(templateElementExt, _node, liftRoot); | 254 _liftNonNativeChildrenIntoContent(templateElementExt, _node, liftRoot); |
| 255 } else if (bootstrapContents) { | 255 } else if (bootstrapContents) { |
| 256 bootstrap(templateElementExt.content); | 256 bootstrap(templateElementExt.content); |
| 257 } | 257 } |
| 258 | 258 |
| 259 return true; | 259 return true; |
| 260 } | 260 } |
| 261 | 261 |
| 262 static final _contentsOwner = new Expando(); | 262 static final _contentsOwner = new Expando(); |
| 263 static final _ownerStagingDocument = new Expando(); | |
| 264 | 263 |
| 265 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner | 264 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner |
| 266 static HtmlDocument _getOrCreateTemplateContentsOwner(Element template) { | 265 static Document _getTemplateContentsOwner(HtmlDocument doc) { |
| 267 var doc = template.ownerDocument; | 266 if (doc.window == null) { |
| 268 if (doc.window == null) return doc; | 267 return doc; |
| 269 | 268 } |
| 270 var d = _contentsOwner[doc]; | 269 var d = _contentsOwner[doc]; |
| 271 if (d == null) { | 270 if (d == null) { |
| 272 // TODO(arv): This should either be a Document or HTMLDocument depending | 271 // TODO(arv): This should either be a Document or HTMLDocument depending |
| 273 // on doc. | 272 // on doc. |
| 274 d = doc.implementation.createHtmlDocument(''); | 273 d = doc.implementation.createHtmlDocument(''); |
| 275 while (d.lastChild != null) { | 274 while (d.lastChild != null) { |
| 276 d.lastChild.remove(); | 275 d.lastChild.remove(); |
| 277 } | 276 } |
| 278 _contentsOwner[doc] = d; | 277 _contentsOwner[doc] = d; |
| 279 } | 278 } |
| 280 return d; | 279 return d; |
| 281 } | 280 } |
| 282 | 281 |
| 283 HtmlDocument _getTemplateStagingDocument() { | |
| 284 if (_stagingDocument == null) { | |
| 285 var owner = _node.ownerDocument; | |
| 286 var doc = _ownerStagingDocument[owner]; | |
| 287 if (doc == null) { | |
| 288 doc = owner.implementation.createHtmlDocument(''); | |
| 289 _ownerStagingDocument[owner] = doc; | |
| 290 } | |
| 291 _stagingDocument = doc; | |
| 292 } | |
| 293 return _stagingDocument; | |
| 294 } | |
| 295 | |
| 296 // For non-template browsers, the parser will disallow <template> in certain | 282 // For non-template browsers, the parser will disallow <template> in certain |
| 297 // locations, so we allow "attribute templates" which combine the template | 283 // locations, so we allow "attribute templates" which combine the template |
| 298 // element with the top-level container node of the content, e.g. | 284 // element with the top-level container node of the content, e.g. |
| 299 // | 285 // |
| 300 // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr> | 286 // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr> |
| 301 // | 287 // |
| 302 // becomes | 288 // becomes |
| 303 // | 289 // |
| 304 // <template repeat="{{ foo }}"> | 290 // <template repeat="{{ foo }}"> |
| 305 // + #document-fragment | 291 // + #document-fragment |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 440 node = node.parentNode; | 426 node = node.parentNode; |
| 441 } | 427 } |
| 442 | 428 |
| 443 // Note: JS code tests that getElementById is present. We can't do that | 429 // Note: JS code tests that getElementById is present. We can't do that |
| 444 // easily, so instead check for the types known to implement it. | 430 // easily, so instead check for the types known to implement it. |
| 445 if (node is Document || node is ShadowRoot || node is SvgSvgElement) { | 431 if (node is Document || node is ShadowRoot || node is SvgSvgElement) { |
| 446 return node; | 432 return node; |
| 447 } | 433 } |
| 448 return null; | 434 return null; |
| 449 } | 435 } |
| OLD | NEW |