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

Unified Diff: tools/dom/templates/html/impl/impl_Element.darttemplate

Issue 14732003: Implement Model-Driven-Views spec for Dart (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: try upload again Created 7 years, 7 months 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 side-by-side diff with in-line comments
Download patch
Index: tools/dom/templates/html/impl/impl_Element.darttemplate
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 08a6b089b464da0343d87cc98e2328cb7c54bd49..89329db7fc0e57ed40fab02081d5c28e1744eec1 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -137,7 +137,7 @@ class _ChildrenElementList extends ListBase<Element> {
}
}
-/**
+/**
* An immutable list containing HTML elements. This list contains some
* additional methods for ease of CSS manipulation on a group of elements.
*/
@@ -625,9 +625,267 @@ $if DART2JS
$else
$endif
+ @Creates('Null')
+ Map<String, StreamSubscription> _attributeBindings;
+
+ // TODO(jmesserly): I'm concerned about adding these to every element.
+ // Conceptually all of these belong on TemplateElement. They are here to
+ // support browsers that don't have <template> yet.
+ // However even in the polyfill they're restricted to certain tags
+ // (see [isTemplate]). So we can probably convert it to a (public) mixin, and
+ // only mix it in to the elements that need it.
+$if DART2JS
+ @Creates('Null') // Set from Dart code; does not instantiate a native type.
+$endif
+ var _model;
+
+$if DART2JS
+ @Creates('Null') // Set from Dart code; does not instantiate a native type.
+$endif
+ _TemplateIterator _templateIterator;
+
+$if DART2JS
+ @Creates('Null') // Set from Dart code; does not instantiate a native type.
+$endif
+ Element _templateInstanceRef;
+
+ // Note: only used if `this is! TemplateElement`
+$if DART2JS
+ @Creates('Null') // Set from Dart code; does not instantiate a native type.
+$endif
+ DocumentFragment _templateContent;
+
+ bool _templateIsDecorated;
+
+ // TODO(jmesserly): should path be optional, and default to empty path?
+ // It is used that way in at least one path in JS TemplateElement tests
+ // (see "BindImperative" test in original JS code).
+ @Experimental
+ void bind(String name, model, String path) {
+ _bindElement(this, name, model, path);
+ }
+
+ // TODO(jmesserly): this is static to work around http://dartbug.com/10166
+ // Similar issue for unbind/unbindAll below.
+ static void _bindElement(Element self, String name, model, String path) {
+ if (self._bindTemplate(name, model, path)) return;
+
+ if (self._attributeBindings == null) {
+ self._attributeBindings = new Map<String, StreamSubscription>();
+ }
+
+ self.attributes.remove(name);
+
+ var changed;
+ if (name.endsWith('?')) {
+ name = name.substring(0, name.length - 1);
+
+ changed = (value) {
+ if (_templateBooleanConversion(value)) {
+ self.attributes[name] = '';
+ } else {
+ self.attributes.remove(name);
+ }
+ };
+ } else {
+ changed = (value) {
+ // TODO(jmesserly): escape value if needed to protect against XSS.
+ // See https://github.com/toolkitchen/mdv/issues/58
+ self.attributes[name] = value == null ? '' : '$value';
+ };
+ }
+
+ self.unbind(name);
+
+ self._attributeBindings[name] =
+ new PathObserver(model, path).bindSync(changed);
+ }
+
+ @Experimental
+ void unbind(String name) {
+ _unbindElement(this, name);
+ }
+
+ static _unbindElement(Element self, String name) {
+ if (self._unbindTemplate(name)) return;
+ if (self._attributeBindings != null) {
+ var binding = self._attributeBindings.remove(name);
+ if (binding != null) binding.cancel();
+ }
+ }
+
+ @Experimental
+ void unbindAll() {
+ _unbindAllElement(this);
+ }
+
+ static void _unbindAllElement(Element self) {
+ self._unbindAllTemplate();
+
+ if (self._attributeBindings != null) {
+ for (var binding in self._attributeBindings.values) {
+ binding.cancel();
+ }
+ self._attributeBindings = null;
+ }
+ }
+
+ // TODO(jmesserly): unlike the JS polyfill, we can't mixin
+ // HTMLTemplateElement at runtime into things that are semantically template
+ // elements. So instead we implement it here with a runtime check.
+ // If the bind succeeds, we return true, otherwise we return false and let
+ // the normal Element.bind logic kick in.
+ bool _bindTemplate(String name, model, String path) {
+ if (isTemplate) {
+ switch (name) {
+ case 'bind':
+ case 'repeat':
+ case 'if':
+ _ensureTemplate();
+ if (_templateIterator == null) {
+ _templateIterator = new _TemplateIterator(this);
+ }
+ _templateIterator.inputs.bind(name, model, path);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool _unbindTemplate(String name) {
+ if (isTemplate) {
+ switch (name) {
+ case 'bind':
+ case 'repeat':
+ case 'if':
+ _ensureTemplate();
+ if (_templateIterator != null) {
+ _templateIterator.inputs.unbind(name);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void _unbindAllTemplate() {
+ if (isTemplate) {
+ unbind('bind');
+ unbind('repeat');
+ unbind('if');
+ }
+ }
+
+ /**
+ * Gets the template this node refers to.
+ * This is only supported if [isTemplate] is true.
+ */
+ @Experimental
+ Element get ref {
+ _ensureTemplate();
+
+ Element ref = null;
+ var refId = attributes['ref'];
+ if (refId != null) {
+ ref = document.getElementById(refId);
+ }
+
+ return ref != null ? ref : _templateInstanceRef;
+ }
+
+ /**
+ * Gets the content of this template.
+ * This is only supported if [isTemplate] is true.
+ */
+ @Experimental
+ DocumentFragment get content {
+ _ensureTemplate();
+ return _templateContent;
+ }
+
+ /**
+ * Creates an instance of the template.
+ * This is only supported if [isTemplate] is true.
+ */
+ @Experimental
+ DocumentFragment createInstance() {
+ _ensureTemplate();
+
+ var template = ref;
+ if (template == null) template = this;
+
+ var instance = _createDeepCloneAndDecorateTemplates(template.content,
+ attributes['syntax']);
+
+ if (TemplateElement._instanceCreated != null) {
+ TemplateElement._instanceCreated.add(instance);
+ }
+ return instance;
+ }
+
+ /**
+ * The data model which is inherited through the tree.
+ * This is only supported if [isTemplate] is true.
+ *
+ * Setting this will destructive propagate the value to all descendant nodes,
+ * and reinstantiate all of the nodes expanded by this template.
+ *
+ * Currently this does not support propagation through Shadow DOMs.
+ */
+ @Experimental
+ get model => _model;
+
+ @Experimental
+ void set model(value) {
+ _ensureTemplate();
+
+ _model = value;
+ _addBindings(this, model);
+ }
+
+ // TODO(jmesserly): const set would be better
+ static const _TABLE_TAGS = const {
+ 'caption': null,
+ 'col': null,
+ 'colgroup': null,
+ 'tbody': null,
+ 'td': null,
+ 'tfoot': null,
+ 'th': null,
+ 'thead': null,
+ 'tr': null,
+ };
+
+ bool get _isAttributeTemplate => attributes.containsKey('template') &&
+ (localName == 'option' || _TABLE_TAGS.containsKey(localName));
+
+ /**
+ * Returns true if this node is a template.
+ *
+ * A node is a template if [tagName] is TEMPLATE, or the node has the
+ * 'template' attribute and this tag supports attribute form for backwards
+ * compatibility with existing HTML parsers. The nodes that can use attribute
+ * form are table elments (THEAD, TBODY, TFOOT, TH, TR, TD, CAPTION, COLGROUP
+ * and COL) and OPTION.
+ */
+ // TODO(jmesserly): this is not a public MDV API, but it seems like a useful
+ // place to document which tags our polyfill considers to be templates.
+ // Otherwise I'd be repeating it in several other places.
+ // See if we can replace this with a TemplateMixin.
+ @Experimental
+ bool get isTemplate => tagName == 'TEMPLATE' || _isAttributeTemplate;
+
+ void _ensureTemplate() {
+ if (!isTemplate) {
+ throw new UnsupportedError('$this is not a template.');
+ }
+ TemplateElement.decorate(this);
+ }
+
$!MEMBERS
}
+
final _START_TAG_REGEXP = new RegExp('<(\\w+)');
class _ElementFactoryProvider {
static const _CUSTOM_PARENT_TAG_MAP = const {
@@ -645,19 +903,6 @@ class _ElementFactoryProvider {
'track' : 'audio',
};
- // TODO(jmesserly): const set would be better
- static const _TABLE_TAGS = const {
- 'caption': null,
- 'col': null,
- 'colgroup': null,
- 'tbody': null,
- 'td': null,
- 'tfoot': null,
- 'th': null,
- 'thead': null,
- 'tr': null,
- };
-
@DomName('Document.createElement')
static Element createElement_html(String html) {
// TODO(jacobr): this method can be made more robust and performant.
@@ -671,7 +916,7 @@ class _ElementFactoryProvider {
final match = _START_TAG_REGEXP.firstMatch(html);
if (match != null) {
tag = match.group(1).toLowerCase();
- if (Device.isIE && _TABLE_TAGS.containsKey(tag)) {
+ if (Device.isIE && Element._TABLE_TAGS.containsKey(tag)) {
return _createTableForIE(html, tag);
}
parentTag = _CUSTOM_PARENT_TAG_MAP[tag];

Powered by Google App Engine
This is Rietveld 408576698