| // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // WARNING: Do not edit - generated code. |
| |
| part of $LIBRARYNAME; |
| |
| /** |
| * Model-Driven Views (MDV)'s native features enables a wide-range of use cases, |
| * but (by design) don't attempt to implement a wide array of specialized |
| * behaviors. |
| * |
| * Enabling these features in MDV is a matter of implementing and registering an |
| * MDV Custom Syntax. A Custom Syntax is an object which contains one or more |
| * delegation functions which implement specialized behavior. This object is |
| * registered with MDV via [Element.bindingDelegate]: |
| * |
| * |
| * HTML: |
| * <template bind> |
| * {{ What!Ever('crazy')->thing^^^I+Want(data) }} |
| * </template> |
| * |
| * Dart: |
| * class MySyntax extends BindingDelegate { |
| * getBinding(model, path, name, node) { |
| * // The magic happens here! |
| * } |
| * } |
| * ... |
| * query('template').bindingDelegate = new MySyntax(); |
| * query('template').model = new MyModel(); |
| * |
| * See <https://github.com/polymer-project/mdv/blob/master/docs/syntax.md> for |
| * more information about Custom Syntax. |
| */ |
| // TODO(jmesserly): move this type to the MDV package? Two issues: we'd lose |
| // type annotation on [Element.bindingDelegate], and "mdv" is normally imported |
| // with a prefix. |
| @Experimental() |
| abstract class BindingDelegate { |
| /** |
| * This syntax method allows for a custom interpretation of the contents of |
| * mustaches (`{{` ... `}}`). |
| * |
| * When a template is inserting an instance, it will invoke this method for |
| * each mustache which is encountered. The function is invoked with four |
| * arguments: |
| * |
| * - [model]: The data context for which this instance is being created. |
| * - [path]: The text contents (trimmed of outer whitespace) of the mustache. |
| * - [name]: The context in which the mustache occurs. Within element |
| * attributes, this will be the name of the attribute. Within text, |
| * this will be 'text'. |
| * - [node]: A reference to the node to which this binding will be created. |
| * |
| * If the method wishes to handle binding, it is required to return an object |
| * which has at least a `value` property that can be observed. If it does, |
| * then MDV will call [Node.bind on the node: |
| * |
| * node.bind(name, retval, 'value'); |
| * |
| * If the 'getBinding' does not wish to override the binding, it should return |
| * null. |
| */ |
| // TODO(jmesserly): I had to remove type annotations from "name" and "node" |
| // Normally they are String and Node respectively. But sometimes it will pass |
| // (int name, CompoundBinding node). That seems very confusing; we may want |
| // to change this API. |
| getBinding(model, String path, name, node) => null; |
| |
| /** |
| * This syntax method allows a syntax to provide an alterate model than the |
| * one the template would otherwise use when producing an instance. |
| * |
| * When a template is about to create an instance, it will invoke this method |
| * The function is invoked with two arguments: |
| * |
| * - [template]: The template element which is about to create and insert an |
| * instance. |
| * - [model]: The data context for which this instance is being created. |
| * |
| * The template element will always use the return value of `getInstanceModel` |
| * as the model for the new instance. If the syntax does not wish to override |
| * the value, it should simply return the `model` value it was passed. |
| */ |
| getInstanceModel(Element template, model) => model; |
| } |
| |
| @Experimental() |
| $(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC { |
| $!MEMBERS |
| |
| // For real TemplateElement use the actual DOM .content field instead of |
| // our polyfilled expando. |
| @Experimental() |
| DocumentFragment get content => _content; |
| |
| |
| /** |
| * The MDV package, if available. |
| * |
| * This can be used to initialize MDV support via: |
| * |
| * import 'dart:html'; |
| * import 'package:mdv/mdv.dart' as mdv; |
| * main() { |
| * mdv.initialize(); |
| * } |
| */ |
| static Function mdvPackage = (node) { |
| throw new UnsupportedError("The MDV package is not available. " |
| "You can enable it with `import 'package:mdv/mdv.dart' as mdv;` and " |
| "`mdv.initialize()`"); |
| }; |
| |
| /** |
| * Ensures proper API and content model for template elements. |
| * |
| * [instanceRef] can be used to set the [Element.ref] property of [template], |
| * and use the ref's content will be used as source when createInstance() is |
| * invoked. |
| * |
| * Returns true if this template was just decorated, or false if it was |
| * already decorated. |
| */ |
| @Experimental() |
| static bool decorate(Element template, [Element instanceRef]) { |
| // == true check because it starts as a null field. |
| if (template._templateIsDecorated == true) return false; |
| |
| _injectStylesheet(); |
| |
| var templateElement = template; |
| templateElement._templateIsDecorated = true; |
| var isNative = templateElement is TemplateElement; |
| var bootstrapContents = isNative; |
| var liftContents = !isNative; |
| var liftRoot = false; |
| |
| if (!isNative && templateElement._isAttributeTemplate) { |
| if (instanceRef != null) { |
| // TODO(jmesserly): this is just an assert in MDV. |
| throw new ArgumentError('instanceRef should not be supplied for ' |
| 'attribute templates.'); |
| } |
| templateElement = _extractTemplateFromAttributeTemplate(template); |
| templateElement._templateIsDecorated = true; |
| isNative = templateElement is TemplateElement; |
| liftRoot = true; |
| } |
| |
| if (!isNative) { |
| var doc = _getTemplateContentsOwner(templateElement.document); |
| templateElement._templateContent = doc.createDocumentFragment(); |
| } |
| |
| if (instanceRef != null) { |
| // template is contained within an instance, its direct content must be |
| // empty |
| templateElement._templateInstanceRef = instanceRef; |
| } else if (liftContents) { |
| _liftNonNativeChildrenIntoContent(templateElement, template, liftRoot); |
| } else if (bootstrapContents) { |
| bootstrap(templateElement.content); |
| } |
| |
| return true; |
| } |
| |
| // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner |
| static Document _getTemplateContentsOwner(HtmlDocument doc) { |
| if (doc.window == null) { |
| return doc; |
| } |
| var d = doc._templateContentsOwner; |
| if (d == null) { |
| // TODO(arv): This should either be a Document or HTMLDocument depending |
| // on doc. |
| d = doc.implementation.createHtmlDocument(''); |
| while (d.lastChild != null) { |
| d.lastChild.remove(); |
| } |
| doc._templateContentsOwner = d; |
| } |
| return d; |
| } |
| |
| // For non-template browsers, the parser will disallow <template> in certain |
| // locations, so we allow "attribute templates" which combine the template |
| // element with the top-level container node of the content, e.g. |
| // |
| // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr> |
| // |
| // becomes |
| // |
| // <template repeat="{{ foo }}"> |
| // + #document-fragment |
| // + <tr class="bar"> |
| // + <td>Bar</td> |
| // |
| static Element _extractTemplateFromAttributeTemplate(Element el) { |
| var template = el.document.$dom_createElement('template'); |
| el.parentNode.insertBefore(template, el); |
| |
| for (var name in el.attributes.keys.toList()) { |
| switch (name) { |
| case 'template': |
| el.attributes.remove(name); |
| break; |
| case 'repeat': |
| case 'bind': |
| case 'ref': |
| template.attributes[name] = el.attributes.remove(name); |
| break; |
| } |
| } |
| |
| return template; |
| } |
| |
| static void _liftNonNativeChildrenIntoContent(Element template, Element el, |
| bool useRoot) { |
| |
| var content = template.content; |
| if (useRoot) { |
| content.append(el); |
| return; |
| } |
| |
| var child; |
| while ((child = el.firstChild) != null) { |
| content.append(child); |
| } |
| } |
| |
| /** |
| * This used to decorate recursively all templates from a given node. |
| * |
| * By default [decorate] will be called on templates lazily when certain |
| * properties such as [model] are accessed, but it can be run eagerly to |
| * decorate an entire tree recursively. |
| */ |
| // TODO(rafaelw): Review whether this is the right public API. |
| @Experimental() |
| static void bootstrap(Node content) { |
| void _bootstrap(template) { |
| if (!TemplateElement.decorate(template)) { |
| bootstrap(template.content); |
| } |
| } |
| |
| // Need to do this first as the contents may get lifted if |node| is |
| // template. |
| // TODO(jmesserly): content is DocumentFragment or Element |
| var descendents = (content as dynamic).queryAll(_allTemplatesSelectors); |
| if (content is Element && (content as Element).isTemplate) { |
| _bootstrap(content); |
| } |
| |
| descendents.forEach(_bootstrap); |
| } |
| |
| static final String _allTemplatesSelectors = |
| 'template, option[template], optgroup[template], ' + |
| Element._TABLE_TAGS.keys.map((k) => "$k[template]").join(", "); |
| |
| static bool _initStyles; |
| |
| static void _injectStylesheet() { |
| if (_initStyles == true) return; |
| _initStyles = true; |
| |
| var style = new StyleElement(); |
| style.text = r''' |
| template, |
| thead[template], |
| tbody[template], |
| tfoot[template], |
| th[template], |
| tr[template], |
| td[template], |
| caption[template], |
| colgroup[template], |
| col[template], |
| option[template] { |
| display: none; |
| }'''; |
| document.head.append(style); |
| } |
| |
| /** |
| * An override to place the contents into content rather than as child nodes. |
| * |
| * See also: |
| * |
| * * <https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#innerhtml-on-templates> |
| */ |
| void setInnerHtml(String html, |
| {NodeValidator validator, NodeTreeSanitizer treeSanitizer}) { |
| text = null; |
| var fragment = createFragment( |
| html, validator: validator, treeSanitizer: treeSanitizer); |
| |
| content.append(fragment); |
| } |
| } |