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