diff --git a/lib/legacy/legacy-element-mixin.html b/lib/legacy/legacy-element-mixin.html
index 8f67b81ee2..8df14be005 100644
--- a/lib/legacy/legacy-element-mixin.html
+++ b/lib/legacy/legacy-element-mixin.html
@@ -291,8 +291,9 @@
* template content.
*/
instanceTemplate(template) {
+ let content = this.constructor._contentForTemplate(template);
let dom = /** @type {DocumentFragment} */
- (document.importNode(template._content || template.content, true));
+ (document.importNode(content, true));
return dom;
}
diff --git a/lib/mixins/property-effects.html b/lib/mixins/property-effects.html
index de11cd4df3..2de01ab393 100644
--- a/lib/mixins/property-effects.html
+++ b/lib/mixins/property-effects.html
@@ -646,106 +646,6 @@
applyBindingValue(inst, info.methodInfo, val);
}
- /**
- * Post-processes template bindings (notes for short) provided by the
- * Bindings library for use by the effects system:
- * - Parses bindings for methods into method `signature` objects
- * - Memoizes the root property for path bindings
- * - Recurses into nested templates and processes those templates and
- * extracts any host properties, which are set to the template's
- * `_content._hostProps`
- * - Adds bindings from the host to elements for any nested
- * template's lexically bound "host properties"; template handling
- * elements can then add accessors to the template for these properties
- * to forward host properties into template instances accordingly.
- *
- * @param {Array} notes List of notes to process; the notes are
- * modified in place.
- * @private
- */
- function processAnnotations(notes) {
- if (!notes._processed) {
- for (let i=0; i} notes List of notes to process for a given template
- * @return {Object} Map of host properties that the template
- * (or any nested templates) uses
- * @private
- */
- function discoverTemplateHostProps(notes) {
- let hostProps = {};
- for (let i=0, n; (i-changed event listener should be
@@ -783,16 +683,9 @@
* @private
*/
function addAnnotatedListener(model, index, property, path, event, negate) {
- let eventName = event ||
- (CaseMap.camelToDashCase(property) + '-changed');
+ event = event || (CaseMap.camelToDashCase(property) + '-changed');
model.__notifyListeners = model.__notifyListeners || [];
- model.__notifyListeners.push({
- index: index,
- property: property,
- path: path,
- event: eventName,
- negate: negate
- });
+ model.__notifyListeners.push({ index, property, path, event, negate });
}
/**
@@ -811,7 +704,7 @@
}
/**
- * On the `inst` element that was previously bound, uses `inst.__templateNotes`
+ * On the `inst` element that was previously bound, uses nodeInfo list
* to setup compound binding storage structures onto the bound
* nodes (`inst.__templateNodes`).
* (`inst._, and 2-way binding event listeners are also added.)
@@ -819,15 +712,15 @@
* @param {Object} inst Instance that bas been previously bound
* @private
*/
- function setupBindings(inst) {
- let notes = inst.__templateNotes;
- if (notes.length) {
- for (let i=0; i < notes.length; i++) {
- let note = notes[i];
+ function setupBindings(inst, nodeInfo) {
+ if (nodeInfo.length) {
+ for (let i=0; i < nodeInfo.length; i++) {
+ let info = nodeInfo[i];
let node = inst.__templateNodes[i];
node.__dataHost = inst;
- if (note.bindings) {
- setupCompoundBinding(note, node);
+ let bindings = info.bindings;
+ if (bindings) {
+ setupCompoundBinding(bindings, node);
}
}
}
@@ -905,25 +798,107 @@
const emptyArray = [];
+ // Regular expressions used for binding
+ const IDENT = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')';
+ const NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')';
+ const SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')';
+ const DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')';
+ const STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')';
+ const ARGUMENT = '(?:' + IDENT + '|' + NUMBER + '|' + STRING + '\\s*' + ')';
+ const ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')';
+ const ARGUMENT_LIST = '(?:' + '\\(\\s*' +
+ '(?:' + ARGUMENTS + '?' + ')' +
+ '\\)\\s*' + ')';
+ const BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; // Group 3
+ const OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*';
+ const CLOSE_BRACKET = '(?:]]|}})';
+ const NEGATE = '(?:(!)\\s*)?'; // Group 2
+ const EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET;
+ const bindingRegex = new RegExp(EXPRESSION, "g");
+
+ function parseBindings(text, hostProps) {
+ let parts = [];
+ let lastIndex = 0;
+ let m;
+ // Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]final"
+ // Regex matches:
+ // Iteration 1: Iteration 2:
+ // m[1]: '{{' '[['
+ // m[2]: '' '!'
+ // m[3]: 'prop' 'compute(foo,bar)'
+ while ((m = bindingRegex.exec(text))) {
+ // Add literal part
+ if (m.index > lastIndex) {
+ parts.push({literal: text.slice(lastIndex, m.index)});
+ }
+ // Add binding part
+ // Mode (one-way or two)
+ let mode = m[1][0];
+ let negate = Boolean(m[2]);
+ let value = m[3].trim();
+ let customEvent, event, colon;
+ if (mode == '{' && (colon = value.indexOf('::')) > 0) {
+ event = value.substring(colon + 2);
+ value = value.substring(0, colon);
+ customEvent = true;
+ }
+ let signature = parseMethod(value, hostProps);
+ let rootProperty;
+ if (!signature) {
+ rootProperty = Polymer.Path.root(value);
+ hostProps[rootProperty] = true;
+ }
+ parts.push({
+ value, mode, negate, event, customEvent, signature, rootProperty,
+ compoundIndex: parts.length
+ });
+ lastIndex = bindingRegex.lastIndex;
+ }
+ // Add a final literal part
+ if (lastIndex && lastIndex < text.length) {
+ let literal = text.substring(lastIndex);
+ if (literal) {
+ parts.push({ literal });
+ }
+ }
+ if (parts.length) {
+ return parts;
+ }
+ }
+
+ function literalFromParts(parts) {
+ let s = '';
+ for (let i=0; i`.
+ *
+ * @override
+ * @param {Node} node Node to parse
+ * @param {Object} templateInfo Template metadata for current template
+ * @param {Object} nodeInfo Node metadata for current template.
+ * @return {boolean} `true` if the visited node added node-specific
+ * metadata to `nodeInfo`
+ * @protected
+ */
+ static _parseTemplateNestedContent(node, templateInfo, nodeInfo) {
+ let noted = super._parseTemplateNestedContent(node, templateInfo, nodeInfo);
+ // Merge host props into outer template and add bindings
+ let hostProps = Object.assign(templateInfo.hostProps, nodeInfo.templateInfo.hostProps);
+ for (let prop in hostProps) {
+ nodeInfo.bindings = nodeInfo.bindings || [];
+ nodeInfo.bindings.push({
+ index: nodeInfo.index,
+ kind: 'property',
+ name: '_host_' + prop,
+ parts: [{
+ mode: '{',
+ value: prop
+ }]
+ });
+ noted = true;
+ }
+ return noted;
+ }
+
}
return PropertyEffects;
diff --git a/lib/mixins/template-stamp.html b/lib/mixins/template-stamp.html
index ac10e38fdd..acd397171f 100644
--- a/lib/mixins/template-stamp.html
+++ b/lib/mixins/template-stamp.html
@@ -16,198 +16,6 @@
'use strict';
- // null-array (shared empty array to avoid null-checks)
- const emptyArray = [];
-
- let bindingRegex = (function() {
- let IDENT = '(?:' + '[a-zA-Z_$][\\w.:$\\-*]*' + ')';
- let NUMBER = '(?:' + '[-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?' + ')';
- let SQUOTE_STRING = '(?:' + '\'(?:[^\'\\\\]|\\\\.)*\'' + ')';
- let DQUOTE_STRING = '(?:' + '"(?:[^"\\\\]|\\\\.)*"' + ')';
- let STRING = '(?:' + SQUOTE_STRING + '|' + DQUOTE_STRING + ')';
- let ARGUMENT = '(?:' + IDENT + '|' + NUMBER + '|' + STRING + '\\s*' + ')';
- let ARGUMENTS = '(?:' + ARGUMENT + '(?:,\\s*' + ARGUMENT + ')*' + ')';
- let ARGUMENT_LIST = '(?:' + '\\(\\s*' +
- '(?:' + ARGUMENTS + '?' + ')' +
- '\\)\\s*' + ')';
- let BINDING = '(' + IDENT + '\\s*' + ARGUMENT_LIST + '?' + ')'; // Group 3
- let OPEN_BRACKET = '(\\[\\[|{{)' + '\\s*';
- let CLOSE_BRACKET = '(?:]]|}})';
- let NEGATE = '(?:(!)\\s*)?'; // Group 2
- let EXPRESSION = OPEN_BRACKET + NEGATE + BINDING + CLOSE_BRACKET;
- return new RegExp(EXPRESSION, "g");
- })();
-
- let insertionPointTag = 'slot';
-
- /**
- * Scans a template to produce an annotation map that stores expression metadata
- * and information that associates the metadata to nodes in a template instance.
- *
- * Supported annotations are:
- * * id attributes
- * * binding annotations in text nodes
- * * double-mustache expressions: {{expression}}
- * * double-bracket expressions: [[expression]]
- * * binding annotations in attributes
- * * attribute-bind expressions: name="{{expression}} || [[expression]]"
- * * property-bind expressions: name*="{{expression}} || [[expression]]"
- * * property-bind expressions: name:="expression"
- * * event annotations
- * * event delegation directives: on-="expression"
- *
- * Generated data-structure:
- * [
- * {
- * id: '',
- * events: [
- * {
- * mode: ['auto'|''],
- * name: ''
- * value: ''
- * }, ...
- * ],
- * bindings: [
- * {
- * kind: ['text'|'attribute'|'property'],
- * mode: ['auto'|''],
- * name: ''
- * value: ''
- * }, ...
- * ],
- * parent: ,
- * index:
- * },
- * ...
- * ]
- *
- * @param {HTMLTemplateElement} template
- * @param {boolean=} stripWhiteSpace
- * @return {Array}
- */
- function parseTemplateAnnotations(template, stripWhiteSpace, ownerDocument) {
- // TODO(kschaaf): File issue and/or remove when fixed
- // hold a reference to content as _content to prevent odd Chrome gc issue
- // nested templates also may receive their content as _content
- let content = (template._content = template._content || template.content);
- // since a template may be re-used, memo-ize notes.
- if (!content._notes) {
- content._notes = [];
- // TODO(sorvell): whitespace and processAnnotations need to be factored
- // into plugins
- ownerDocument = ownerDocument || template.ownerDocument;
- parseNodeAnnotations(content, content._notes,
- stripWhiteSpace || template.hasAttribute('strip-whitespace'), ownerDocument);
- }
- return content._notes;
- }
-
- // add annotations gleaned from subtree at `node` to `list`
- function parseNodeAnnotations(node, list, stripWhiteSpace, ownerDocument) {
- return node.nodeType === Node.TEXT_NODE ?
- parseTextNodeAnnotation(node, list) :
- parseElementAnnotations(node, list, stripWhiteSpace, ownerDocument);
- }
-
- function parseBindings(text) {
- let parts = [];
- let lastIndex = 0;
- let m;
- // Example: "literal1{{prop}}literal2[[!compute(foo,bar)]]final"
- // Regex matches:
- // Iteration 1: Iteration 2:
- // m[1]: '{{' '[['
- // m[2]: '' '!'
- // m[3]: 'prop' 'compute(foo,bar)'
- while ((m = bindingRegex.exec(text)) !== null) {
- // Add literal part
- if (m.index > lastIndex) {
- parts.push({literal: text.slice(lastIndex, m.index)});
- }
- // Add binding part
- // Mode (one-way or two)
- let mode = m[1][0];
- let negate = Boolean(m[2]);
- let value = m[3].trim();
- let customEvent, notifyEvent, colon;
- if (mode == '{' && (colon = value.indexOf('::')) > 0) {
- notifyEvent = value.substring(colon + 2);
- value = value.substring(0, colon);
- customEvent = true;
- }
- parts.push({
- compoundIndex: parts.length,
- value: value,
- mode: mode,
- negate: negate,
- event: notifyEvent,
- customEvent: customEvent
- });
- lastIndex = bindingRegex.lastIndex;
- }
- // Add a final literal part
- if (lastIndex && lastIndex < text.length) {
- let literal = text.substring(lastIndex);
- if (literal) {
- parts.push({
- literal: literal
- });
- }
- }
- if (parts.length) {
- return parts;
- }
- }
-
- function literalFromParts(parts) {
- let s = '';
- for (let i=0; i',
+ * // `on-event="handler"` metadata
+ * events: [
+ * {
+ * name: ''
+ * value: ''
+ * }, ...
+ * ],
+ * // Notes when the template contained a `` for shady DOM
+ * // optimization purposes
+ * hasInsertionPoint: ,
+ * // For nested nodes, nested template metadata
+ * templateInfo: ,
+ * // Metadata to allow efficient retrieval of instanced node
+ * // corresponding to this metadata
+ * parent: ,
+ * index:
+ * },
+ * ...
+ * ],
+ * // When true, the template had the `strip-whitespace` attribute
+ * // or was nested in a template with that setting
+ * stripWhitespace:
+ * // For nested templates, nested template content is moved into
+ * // a document fragment stored here; this is an optimization to
+ * // avoid the cost of nested template cloning
+ * content:
+ * }
+ *
+ * This method kicks off a recursive treewalk as follows:
+ *
+ * _parseTemplate <------------------+
+ * _parseTemplateNode <-----------|--+
+ * _parseTemplateNestedContent --+ |
+ * _parseTemplateChildNodes --------+
+ * _parseTemplateNodeAttributes
+ * _parseTemplateNodeAttribute
+ *
+ * These methods may be overridden to add custom metadata about templates
+ * to either `templateInfo` or `nodeInfo`.
+ *
+ * Note that this method may be destructive to the template, in that
+ * e.g. event annotations may be removed after being noted in the
+ * template metadata.
+ *
+ * @param {HTMLTemplateElement} template Template to parse
+ * @param {Object=} outerTemplateInfo Template metadata from the outer
+ * template, for parsing nested templates
+ * @return {Object} Parsed template metadata
+ */
+ static _parseTemplate(template, outerTemplateInfo) {
+ // since a template may be re-used, memo-ize metadata
+ if (!template._templateInfo) {
+ let templateInfo = template._templateInfo || (template._templateInfo = {});
+ templateInfo.nodeInfoList = [];
+ templateInfo.stripWhiteSpace =
+ (outerTemplateInfo && outerTemplateInfo.stripWhiteSpace) ||
+ template.hasAttribute('strip-whitespace');
+ this._parseTemplateNode(template.content, templateInfo, {parent: null});
+ }
+ return template._templateInfo;
+ }
+
+ /**
+ * Parses template node and adds template and node metadata based on
+ * the current node, and its `childNodes` and `attributes`.
+ *
+ * This method may be overridden to add custom node or template specific
+ * metadata based on this node.
+ *
+ * @param {Node} node Node to parse
+ * @param {Object} templateInfo Template metadata for current template
+ * @param {Object} nodeInfo Node metadata for current template.
+ * @return {boolean} `true` if the visited node added node-specific
+ * metadata to `nodeInfo`
+ */
+ static _parseTemplateNode(node, templateInfo, nodeInfo) {
+ let noted;
+ if (node.localName == 'template' && !node.hasAttribute('preserve-content')) {
+ noted = this._parseTemplateNestedContent(node, templateInfo, nodeInfo) || noted;
+ } else if (node.localName === 'slot') {
+ // For ShadyDom optimization, indicating there is an insertion point
+ templateInfo.hasInsertionPoint = true;
+ }
+ if (node.firstChild) {
+ noted = this._parseTemplateChildNodes(node, templateInfo, nodeInfo) || noted;
+ }
+ if (node.hasAttributes && node.hasAttributes()) {
+ noted = this._parseTemplateNodeAttributes(node, templateInfo, nodeInfo) || noted;
+ }
+ return noted;
+ }
+
+ /**
+ * Parses template child nodes for the given root node.
+ *
+ * This method also wraps whitelisted legacy template extensions
+ * (`is="dom-if"` and `is="dom-repeat"`) with their equivalent element
+ * wrappers, collapses text nodes, and strips whitespace from the template
+ * if the `templateInfo.stripWhitespace` setting was provided.
+ *
+ * @param {Node} root Root node whose `childNodes` will be parsed
+ * @param {Object} templateInfo Template metadata for current template
+ * @param {Object} nodeInfo Node metadata for current template.
+ */
+ static _parseTemplateChildNodes(root, templateInfo, nodeInfo) {
+ for (let node=root.firstChild, index=0, next; node; node=next) {
+ // Wrap templates
+ if (node.localName == 'template') {
+ node = wrapTemplateExtension(node);
+ }
+ // collapse adjacent textNodes: fixes an IE issue that can cause
+ // text nodes to be inexplicably split =(
+ // note that root.normalize() should work but does not so we do this
+ // manually.
+ next = node.nextSibling;
+ if (node.nodeType === Node.TEXT_NODE) {
+ let n = next;
+ while (n && (n.nodeType === Node.TEXT_NODE)) {
+ node.textContent += n.textContent;
+ next = n.nextSibling;
+ root.removeChild(n);
+ n = next;
+ }
+ // optionally strip whitespace
+ if (templateInfo.stripWhiteSpace && !node.textContent.trim()) {
+ root.removeChild(node);
+ continue;
+ }
+ }
+ let childInfo = { index, parent: nodeInfo };
+ if (this._parseTemplateNode(node, templateInfo, childInfo)) {
+ templateInfo.nodeInfoList.push(childInfo);
+ }
+ index++;
+ }
+ }
+
+ /**
+ * Parses template content for the given nested ``.
+ *
+ * Nested template info is stored as `templateInfo` in the current node's
+ * `nodeInfo`. `template.content` is removed and stored in `templateInfo`.
+ * It will then be the responsibility of the host to set it back to the
+ * template and for users stamping nested templates to use the
+ * `_contentForTemplate` method to retrieve the content for this template
+ * (an optimization to avoid the cost of cloning nested template content).
+ *
+ * @param {Node} node Node to parse
+ * @param {Object} templateInfo Template metadata for current template
+ * @param {Object} nodeInfo Node metadata for current template.
+ * @return {boolean} `true` if the visited node added node-specific
+ * metadata to `nodeInfo`
+ */
+ static _parseTemplateNestedContent(node, outerTemplateInfo, nodeInfo) {
+ let templateInfo = this._parseTemplate(node, outerTemplateInfo);
+ let content = templateInfo.content =
+ node.content.ownerDocument.createDocumentFragment();
+ content.appendChild(node.content);
+ nodeInfo.templateInfo = templateInfo;
+ return true;
+ }
+
+ /**
+ * Parses template node attributes and adds node metadata to `nodeInfo`
+ * for nodes of interest.
+ *
+ * @param {Node} node Node to parse
+ * @param {Object} templateInfo Template metadata for current template
+ * @param {Object} nodeInfo Node metadata for current template.
+ * @return {boolean} `true` if the visited node added node-specific
+ * metadata to `nodeInfo`
+ */
+ static _parseTemplateNodeAttributes(node, templateInfo, nodeInfo) {
+ // Make copy of original attribute list, since the order may change
+ // as attributes are added and removed
+ let noted;
+ let attrs = Array.from(node.attributes);
+ for (let i=attrs.length-1, a; (a=attrs[i]); i--) {
+ noted = this._parseTemplateNodeAttribute(node, templateInfo, nodeInfo, a.name, a.value) || noted;
+ }
+ return noted;
+ }
+
+ /**
+ * Parses a single template node attribute and adds node metadata to
+ * `nodeInfo` for attributes of interest.
+ *
+ * This implementation adds metadata for `on-event="handler"` attributes
+ * and `id` attributes.
+ *
+ * @param {Node} node Node to parse
+ * @param {Object} templateInfo Template metadata for current template
+ * @param {Object} nodeInfo Node metadata for current template.
+ * @param {string} name Attribute name
+ * @param {string} name Attribute value
+ * @return {boolean} `true` if the visited node added node-specific
+ * metadata to `nodeInfo`
+ */
+ static _parseTemplateNodeAttribute(node, templateInfo, nodeInfo, name, value) {
+ // events (on-*)
+ if (name.slice(0, 3) === 'on-') {
+ node.removeAttribute(name);
+ nodeInfo.events = nodeInfo.events || [];
+ nodeInfo.events.push({
+ name: name.slice(3),
+ value
+ });
+ return true;
+ }
+ // static id
+ else if (name === 'id') {
+ nodeInfo.id = value;
+ return true;
+ }
+ }
+
+ /**
+ * Returns the `content` document fragment for a given template.
+ *
+ * For nested templates, Polymer performs an optimization to cache nested
+ * template content to avoid the cost of cloning deeply nested templates.
+ * This method retrieves the cached content for a given template.
+ *
+ * @param {HTMLTemplateElement} template Template to retrieve `content` for
+ * @return {DocumentFragment} Content fragment
+ */
+ static _contentForTemplate(template) {
+ let templateInfo = template.__templateInfo;
+ return (templateInfo && templateInfo.content) || template.content;
}
/**
@@ -466,17 +375,13 @@
* containing the cloned dom.
*
* The template is parsed (once and memoized) using this library's
- * template parsing features, which identify nodes with declarative
- * event listeners (`on-...``), `id`'s, ``s, and bindings
- * (`{{...}}` and `[[...]]`). This template metadata ("notes")
- * are stored as `this.__templateNotes`, and any nodes identified in
- * notes are collected for this instance into `__templateNodes` in
- * the same order as the notes array.
- *
- * Finally, this method generates an "id map" for all nodes with id's
- * under `this.$`, passes template content back to templates as `_content`
- * (a performance optimization to avoid deep template cloning), and
- * installs declarative event listeners.
+ * template parsing features, and provides the following value-added
+ * features:
+ * * Adds declarative event listners for `on-event="handler"` attributes
+ * * Generates an "id map" for all nodes with id's under `this.$`
+ * * Passes template info including `content` back to templates as
+ * `_templateInfo` (a performance optimization to avoid deep template
+ * cloning)
*
* Note that the memoized template parsing process is destructive to the
* template: attributes for bindings and declarative event listeners are
@@ -487,6 +392,7 @@
* not support stamping multiple templates per element instance).
*
* @param {HTMLTemplateElement} template Template to stamp
+ * @return {DocumentFragment} Cloned template content
*/
_stampTemplate(template) {
// Polyfill support: bootstrap the template if it has not already been
@@ -494,32 +400,34 @@
window.HTMLTemplateElement && HTMLTemplateElement.decorate) {
HTMLTemplateElement.decorate(template);
}
- let notes = this.__templateNotes = this._parseTemplateAnnotations(template);
- let dom = document.importNode(template._content || template.content, true);
+ let templateInfo = this.constructor._parseTemplate(template);
+ let nodeInfo = templateInfo.nodeInfoList;
+ let content = templateInfo.content || template.content;
+ let dom = document.importNode(content, true);
// NOTE: ShadyDom optimization indicating there is an insertion point
- dom.__noInsertionPoint = !notes._hasInsertionPoint;
+ dom.__noInsertionPoint = !templateInfo.hasInsertionPoint;
this.$ = {};
- this.__templateNodes = new Array(notes.length);
- for (let i=0, l=notes.length, note, node; (iinstances effects
// and host <- template effects
- let hostProps = template._content._hostProps;
+ let hostProps = templateInfo.hostProps;
for (let prop in hostProps) {
klass.prototype._addPropertyEffect('_host_' + prop,
klass.prototype.PROPERTY_EFFECT_TYPES.PROPAGATE,
@@ -250,7 +251,8 @@
}
function addNotifyEffects(klass, template, options) {
- let hostProps = template._content._hostProps || {};
+ let templateInfo = template._templateInfo;
+ let hostProps = templateInfo.hostProps || {};
for (let iprop in options.instanceProps) {
delete hostProps[iprop];
let userNotifyInstanceProp = options.notifyInstanceProp;
@@ -388,14 +390,14 @@
throw new Error('A can only be templatized once');
}
template.__templatizeOwner = owner;
- // Ensure template has _content
- template._content = template._content || template.content;
+ let templateInfo = template._templateInfo;
// Get memoized base class for the prototypical template, which
// includes property effects for binding template & forwarding
- let baseClass = template._content.__templatizeInstanceClass;
+ let baseClass = templateInfo && templateInfo.templatizeInstanceClass;
if (!baseClass) {
- baseClass = template._content.__templatizeInstanceClass =
- createTemplatizerClass(template, options);
+ baseClass = createTemplatizerClass(template, options);
+ templateInfo = template._templateInfo;
+ templateInfo.templatizeInstanceClass = baseClass;
}
// Host property forwarding must be installed onto template instance
addPropagateEffects(template, options);
@@ -404,7 +406,7 @@
klass.prototype._methodHost = findMethodHost(template);
klass.prototype.__dataHost = template;
klass.prototype.__templatizeOwner = owner;
- klass.prototype.__hostProps = template._content._hostProps;
+ klass.prototype.__hostProps = templateInfo.hostProps;
return klass;
},