| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 // WARNING: Do not edit - generated code. | 5 // WARNING: Do not edit - generated code. |
| 6 | 6 |
| 7 part of $LIBRARYNAME; | 7 part of $LIBRARYNAME; |
| 8 | 8 |
| 9 |
| 10 /** |
| 11 * Model-Driven Views (MDV)'s native features enables a wide-range of use cases, |
| 12 * but (by design) don't attempt to implement a wide array of specialized |
| 13 * behaviors. |
| 14 * |
| 15 * Enabling these features in MDV is a matter of implementing and registering an |
| 16 * MDV Custom Syntax. A Custom Syntax is an object which contains one or more |
| 17 * delegation functions which implement specialized behavior. This object is |
| 18 * registered with MDV via [TemplateElement.syntax]: |
| 19 * |
| 20 * |
| 21 * HTML: |
| 22 * <template bind syntax="MySyntax"> |
| 23 * {{ What!Ever('crazy')->thing^^^I+Want(data) }} |
| 24 * </template> |
| 25 * |
| 26 * Dart: |
| 27 * class MySyntax extends CustomBindingSyntax { |
| 28 * getBinding(model, path, name, node) { |
| 29 * // The magic happens here! |
| 30 * } |
| 31 * } |
| 32 * |
| 33 * ... |
| 34 * |
| 35 * TemplateElement.syntax['MySyntax'] = new MySyntax(); |
| 36 * |
| 37 * See <https://github.com/polymer-project/mdv/blob/master/docs/syntax.md> for |
| 38 * more information about Custom Syntax. |
| 39 */ |
| 40 // TODO(jmesserly): if this is just one method, a function type would make it |
| 41 // more Dart-friendly. |
| 42 @Experimental |
| 43 abstract class CustomBindingSyntax { |
| 44 /** |
| 45 * This syntax method allows for a custom interpretation of the contents of |
| 46 * mustaches (`{{` ... `}}`). |
| 47 * |
| 48 * When a template is inserting an instance, it will invoke this method for |
| 49 * each mustache which is encountered. The function is invoked with four |
| 50 * arguments: |
| 51 * |
| 52 * - [model]: The data context for which this instance is being created. |
| 53 * - [path]: The text contents (trimmed of outer whitespace) of the mustache. |
| 54 * - [name]: The context in which the mustache occurs. Within element |
| 55 * attributes, this will be the name of the attribute. Within text, |
| 56 * this will be 'text'. |
| 57 * - [node]: A reference to the node to which this binding will be created. |
| 58 * |
| 59 * If the method wishes to handle binding, it is required to return an object |
| 60 * which has at least a `value` property that can be observed. If it does, |
| 61 * then MDV will call [Node.bind on the node: |
| 62 * |
| 63 * node.bind(name, retval, 'value'); |
| 64 * |
| 65 * If the 'getBinding' does not wish to override the binding, it should return |
| 66 * null. |
| 67 */ |
| 68 // TODO(jmesserly): I had to remove type annotations from "name" and "node" |
| 69 // Normally they are String and Node respectively. But sometimes it will pass |
| 70 // (int name, CompoundBinding node). That seems very confusing; we may want |
| 71 // to change this API. |
| 72 getBinding(model, String path, name, node) => null; |
| 73 |
| 74 /** |
| 75 * This syntax method allows a syntax to provide an alterate model than the |
| 76 * one the template would otherwise use when producing an instance. |
| 77 * |
| 78 * When a template is about to create an instance, it will invoke this method |
| 79 * The function is invoked with two arguments: |
| 80 * |
| 81 * - [template]: The template element which is about to create and insert an |
| 82 * instance. |
| 83 * - [model]: The data context for which this instance is being created. |
| 84 * |
| 85 * The template element will always use the return value of `getInstanceModel` |
| 86 * as the model for the new instance. If the syntax does not wish to override |
| 87 * the value, it should simply return the `model` value it was passed. |
| 88 */ |
| 89 getInstanceModel(Element template, model) => model; |
| 90 |
| 91 /** |
| 92 * This syntax method allows a syntax to provide an alterate expansion of |
| 93 * the [template] contents. When the template wants to create an instance, |
| 94 * it will call this method with the template element. |
| 95 * |
| 96 * By default this will call `template.createInstance()`. |
| 97 */ |
| 98 getInstanceFragment(Element template) => template.createInstance(); |
| 99 } |
| 100 |
| 101 |
| 9 @Experimental | 102 @Experimental |
| 10 $(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { | 103 $(ANNOTATIONS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { |
| 11 $!MEMBERS | 104 $!MEMBERS |
| 12 | 105 |
| 13 // For real TemplateElement use the actual DOM .content field instead of | 106 // For real TemplateElement use the actual DOM .content field instead of |
| 14 // our polyfilled expando. | 107 // our polyfilled expando. |
| 15 @Experimental | 108 @Experimental |
| 16 DocumentFragment get content => $dom_content; | 109 DocumentFragment get content => $dom_content; |
| 17 | 110 |
| 18 static StreamController<DocumentFragment> _instanceCreated; | |
| 19 | 111 |
| 20 /** | 112 /** |
| 21 * *Warning*: This is an implementation helper for Model-Driven Views and | 113 * The MDV package, if available. |
| 22 * should not be used in your code. | |
| 23 * | 114 * |
| 24 * This event is fired whenever a template is instantiated via | 115 * This can be used to initialize MDV support via: |
| 25 * [createInstance]. | 116 * |
| 117 * import 'dart:html'; |
| 118 * import 'package:mdv/mdv.dart' as mdv; |
| 119 * main() { |
| 120 * mdv.initialize(); |
| 121 * } |
| 26 */ | 122 */ |
| 27 // TODO(rafaelw): This is a hack, and is neccesary for the polyfill | 123 static Function mdvPackage = (node) { |
| 28 // because custom elements are not upgraded during clone() | 124 throw new UnsupportedError("The MDV package is not available. " |
| 29 @Experimental | 125 "You can enable it with `import 'package:mdv/mdv.dart' as mdv;` and " |
| 30 static Stream<DocumentFragment> get instanceCreated { | 126 "`mdv.initialize()`"); |
| 31 if (_instanceCreated == null) { | 127 }; |
| 32 _instanceCreated = new StreamController<DocumentFragment>(sync: true); | |
| 33 } | |
| 34 return _instanceCreated.stream; | |
| 35 } | |
| 36 | 128 |
| 37 /** | 129 /** |
| 38 * Ensures proper API and content model for template elements. | 130 * Ensures proper API and content model for template elements. |
| 39 * | 131 * |
| 40 * [instanceRef] can be used to set the [Element.ref] property of [template], | 132 * [instanceRef] can be used to set the [Element.ref] property of [template], |
| 41 * and use the ref's content will be used as source when createInstance() is | 133 * and use the ref's content will be used as source when createInstance() is |
| 42 * invoked. | 134 * invoked. |
| 43 * | 135 * |
| 44 * Returns true if this template was just decorated, or false if it was | 136 * Returns true if this template was just decorated, or false if it was |
| 45 * already decorated. | 137 * already decorated. |
| 46 */ | 138 */ |
| 47 @Experimental | 139 @Experimental |
| 48 static bool decorate(Element template, [Element instanceRef]) { | 140 static bool decorate(Element template, [Element instanceRef]) { |
| 49 // == true check because it starts as a null field. | 141 // == true check because it starts as a null field. |
| 50 if (template._templateIsDecorated == true) return false; | 142 if (template._templateIsDecorated == true) return false; |
| 51 | 143 |
| 52 template._templateIsDecorated = true; | 144 template._templateIsDecorated = true; |
| 53 | 145 |
| 54 _injectStylesheet(); | 146 _injectStylesheet(); |
| 55 | 147 |
| 56 // Create content | 148 // Create content |
| 57 if (template is! TemplateElement) { | 149 if (template is! TemplateElement) { |
| 58 var doc = _Bindings._getTemplateContentsOwner(template.document); | 150 var doc = _getTemplateContentsOwner(template.document); |
| 59 template._templateContent = doc.createDocumentFragment(); | 151 template._templateContent = doc.createDocumentFragment(); |
| 60 } | 152 } |
| 61 | 153 |
| 62 if (instanceRef != null) { | 154 if (instanceRef != null) { |
| 63 template._templateInstanceRef = instanceRef; | 155 template._templateInstanceRef = instanceRef; |
| 64 return true; // content is empty. | 156 return true; // content is empty. |
| 65 } | 157 } |
| 66 | 158 |
| 67 if (template is TemplateElement) { | 159 if (template is TemplateElement) { |
| 68 bootstrap(template.content); | 160 bootstrap(template.content); |
| 69 } else { | 161 } else { |
| 70 _Bindings._liftNonNativeChildrenIntoContent(template); | 162 _liftNonNativeChildrenIntoContent(template); |
| 71 } | 163 } |
| 72 | 164 |
| 73 return true; | 165 return true; |
| 74 } | 166 } |
| 75 | 167 |
| 168 // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#
dfn-template-contents-owner |
| 169 static Document _getTemplateContentsOwner(HtmlDocument doc) { |
| 170 if (doc.window == null) { |
| 171 return doc; |
| 172 } |
| 173 var d = doc._templateContentsOwner; |
| 174 if (d == null) { |
| 175 // TODO(arv): This should either be a Document or HTMLDocument depending |
| 176 // on doc. |
| 177 d = doc.implementation.createHtmlDocument(''); |
| 178 while (d.lastChild != null) { |
| 179 d.lastChild.remove(); |
| 180 } |
| 181 doc._templateContentsOwner = d; |
| 182 } |
| 183 return d; |
| 184 } |
| 185 |
| 186 static Element _cloneAndSeperateAttributeTemplate(Element templateElement) { |
| 187 var clone = templateElement.clone(false); |
| 188 var attributes = templateElement.attributes; |
| 189 for (var name in attributes.keys.toList()) { |
| 190 switch (name) { |
| 191 case 'template': |
| 192 case 'repeat': |
| 193 case 'bind': |
| 194 case 'ref': |
| 195 clone.attributes.remove(name); |
| 196 break; |
| 197 default: |
| 198 attributes.remove(name); |
| 199 break; |
| 200 } |
| 201 } |
| 202 |
| 203 return clone; |
| 204 } |
| 205 |
| 206 static void _liftNonNativeChildrenIntoContent(Element templateElement) { |
| 207 var content = templateElement.content; |
| 208 |
| 209 if (!templateElement._isAttributeTemplate) { |
| 210 var child; |
| 211 while ((child = templateElement.firstChild) != null) { |
| 212 content.append(child); |
| 213 } |
| 214 return; |
| 215 } |
| 216 |
| 217 // For attribute templates we copy the whole thing into the content and |
| 218 // we move the non template attributes into the content. |
| 219 // |
| 220 // <tr foo template> |
| 221 // |
| 222 // becomes |
| 223 // |
| 224 // <tr template> |
| 225 // + #document-fragment |
| 226 // + <tr foo> |
| 227 // |
| 228 var newRoot = _cloneAndSeperateAttributeTemplate(templateElement); |
| 229 var child; |
| 230 while ((child = templateElement.firstChild) != null) { |
| 231 newRoot.append(child); |
| 232 } |
| 233 content.append(newRoot); |
| 234 } |
| 235 |
| 76 /** | 236 /** |
| 77 * This used to decorate recursively all templates from a given node. | 237 * This used to decorate recursively all templates from a given node. |
| 78 * | 238 * |
| 79 * By default [decorate] will be called on templates lazily when certain | 239 * By default [decorate] will be called on templates lazily when certain |
| 80 * properties such as [model] are accessed, but it can be run eagerly to | 240 * properties such as [model] are accessed, but it can be run eagerly to |
| 81 * decorate an entire tree recursively. | 241 * decorate an entire tree recursively. |
| 82 */ | 242 */ |
| 83 // TODO(rafaelw): Review whether this is the right public API. | 243 // TODO(rafaelw): Review whether this is the right public API. |
| 84 @Experimental | 244 @Experimental |
| 85 static void bootstrap(Node content) { | 245 static void bootstrap(Node content) { |
| 86 _Bindings._bootstrapTemplatesRecursivelyFrom(content); | 246 void _bootstrap(template) { |
| 247 if (!TemplateElement.decorate(template)) { |
| 248 bootstrap(template.content); |
| 249 } |
| 250 } |
| 251 |
| 252 // Need to do this first as the contents may get lifted if |node| is |
| 253 // template. |
| 254 // TODO(jmesserly): content is DocumentFragment or Element |
| 255 var descendents = (content as dynamic).queryAll(_allTemplatesSelectors); |
| 256 if (content is Element && (content as Element).isTemplate) { |
| 257 _bootstrap(content); |
| 258 } |
| 259 |
| 260 descendents.forEach(_bootstrap); |
| 87 } | 261 } |
| 88 | 262 |
| 89 /** | 263 static final String _allTemplatesSelectors = 'template, option[template], ' + |
| 90 * Binds all mustaches recursively starting from the [root] node. | 264 Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", "); |
| 91 * | |
| 92 * Note: this is not an official Model-Driven-Views API; it is intended to | |
| 93 * support binding the [ShadowRoot]'s content to a model. | |
| 94 */ | |
| 95 // TODO(jmesserly): this is needed to avoid two <template> nodes when using | |
| 96 // bindings in a custom element's template. See also: | |
| 97 // https://github.com/polymer-project/polymer/blob/master/src/bindMDV.js#L68 | |
| 98 // Called from: | |
| 99 // https://github.com/polymer-project/polymer/blob/master/src/register.js#L99 | |
| 100 @Experimental | |
| 101 static void bindModel(Node root, model, [CustomBindingSyntax syntax]) { | |
| 102 _Bindings._addBindings(root, model, syntax); | |
| 103 } | |
| 104 | 265 |
| 105 static bool _initStyles; | 266 static bool _initStyles; |
| 106 | 267 |
| 107 static void _injectStylesheet() { | 268 static void _injectStylesheet() { |
| 108 if (_initStyles == true) return; | 269 if (_initStyles == true) return; |
| 109 _initStyles = true; | 270 _initStyles = true; |
| 110 | 271 |
| 111 var style = new StyleElement(); | 272 var style = new StyleElement(); |
| 112 style.text = r''' | 273 style.text = r''' |
| 113 template, | 274 template, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 126 document.head.append(style); | 287 document.head.append(style); |
| 127 } | 288 } |
| 128 | 289 |
| 129 /** | 290 /** |
| 130 * A mapping of names to Custom Syntax objects. See [CustomBindingSyntax] for | 291 * A mapping of names to Custom Syntax objects. See [CustomBindingSyntax] for |
| 131 * more information. | 292 * more information. |
| 132 */ | 293 */ |
| 133 @Experimental | 294 @Experimental |
| 134 static Map<String, CustomBindingSyntax> syntax = {}; | 295 static Map<String, CustomBindingSyntax> syntax = {}; |
| 135 } | 296 } |
| OLD | NEW |