diff --git a/element/element.js b/element/element.js
index 9386722..4523f19 100644
--- a/element/element.js
+++ b/element/element.js
@@ -76,7 +76,7 @@ function assignProperty(properties, entry) {
return properties;
}
-export default function LiteralElement(tag, lifecycle = {}, properties = {}, consts = {}) {
+export default function LiteralElement(tag, lifecycle = {}, properties = {}) {
if (window.DEBUG && typeof src === 'string' && !/^#/.test(src)) {
console.error('TODO: Support external templates?');
// requestTemplate(value).then((template) => {
@@ -100,16 +100,27 @@ export default function LiteralElement(tag, lifecycle = {}, properties = {}, con
if (window.DEBUG) document.head.appendChild(create('comment', ' Templates for ' + name));
document.head.append.apply(document.head, templates);
- const life = {
+ // Assemble properties
+ const props = properties ?
+ entries(properties).reduce(assignProperty, {}) :
+ {} ;
+
+ const message = window.DEBUG ?
+ 'literal element stephen.band/literal/element/' :
+ //+ (keys(consts).length ? '\n Imports ' + keys(scope).join(', ') : '') :
+ '' ;
+
+ // tag, lifecycle, properties, stylesheet, message
+ return element(tag, {
// DEBUG stylesheet for in-DOM prints of errors and logs
shadow: window.DEBUG ? '' : '',
construct: function(shadow, internals) {
- // Render data
+ // Data object
internals.object = {};
- // template, parent, consts, data, options
- const renderer = new Literal(template, this, assign({ host: this, shadow, internals }, consts), undefined);
+ const consts = { host: this, shadow, internals };
+ const renderer = Literal.fromTemplate(template, this, consts);
shadow.appendChild(renderer.content);
// Call lifecycle.construct()
@@ -152,18 +163,5 @@ export default function LiteralElement(tag, lifecycle = {}, properties = {}, con
disable: lifecycle.disable && function disable(shadow, internals) { lifecycle.disable.call(this, shadow, internals, internals.data); },
reset: lifecycle.reset && function reset(shadow, internals) { lifecycle.reset.call(this, shadow, internals, internals.data); },
restore: lifecycle.restore && function restore(shadow, internals) { lifecycle.restore.call(this, shadow, internals, internals.data); }
- };
-
- // Assemble properties
- const props = properties ?
- entries(properties).reduce(assignProperty, {}) :
- {} ;
-
- const message = window.DEBUG ?
- 'literal element stephen.band/literal/element/'
- + (keys(consts).length ? '\n Imports ' + keys(scope).join(', ') : '') :
- '' ;
-
- // tag, lifecycle, properties, stylesheet, message
- return element(tag, life, props, null, message);
+ }, props, null, message);
}
diff --git a/literal-html/module.js b/literal-html/module.js
index 7fb135c..7ef4184 100644
--- a/literal-html/module.js
+++ b/literal-html/module.js
@@ -11,7 +11,7 @@ import Signal from '../../fn/modules/signal.js';
import element, { getInternals as Internals } from '../../dom/modules/element.js';
import assignDataset from '../modules/dom/assign-dataset.js';
import requestData from '../modules/request-data.js';
-import Template from '../modules/template.js';
+import DOMRenderer from '../modules/template.js';
import { printError } from '../modules/scope/print.js';
const assign = Object.assign;
@@ -32,7 +32,7 @@ export default element('', {
internals.initialised = false;
internals.pushed = false;
internals.data = Signal.of();
- internals.renderer = new Template(this, this.parentElement);
+ internals.renderer = DOMRenderer.fromTemplate(this, this.parentElement);
},
connect: function(shadow) {
diff --git a/modules/compile/compile-attribute.js b/modules/compile/compile-attribute.js
index 9d4697d..ba5ee9a 100644
--- a/modules/compile/compile-attribute.js
+++ b/modules/compile/compile-attribute.js
@@ -21,7 +21,7 @@ const assign = Object.assign;
compileAttributes(array, element, attribute, path, options[, debug])
**/
-export default function compileAttribute(array, element, attribute, path, options, template) {
+export default function compileAttribute(array, element, attribute, path, options, debug) {
const source = attribute.value;
if (!isLiteralString(source)) { return; }
@@ -46,10 +46,10 @@ export default function compileAttribute(array, element, attribute, path, option
property === 'checked' ? CheckedRenderer :
typeof element[property] === 'boolean' ? BooleanRenderer :
typeof element[property] === 'object' && element[property].add && element[property].remove ? TokensRenderer :
- AttributeRenderer :
- AttributeRenderer ;
+ AttributeRenderer :
+ AttributeRenderer ;
- const target = { template, path, name, source, upgradeable, Renderer };
+ const target = { path, name, source, Renderer, upgradeable, debug };
if (window.DEBUG) {
const code = truncate(64, '<'
diff --git a/modules/compile/compile-node.js b/modules/compile/compile-node.js
index 5a9206f..fd103b0 100644
--- a/modules/compile/compile-node.js
+++ b/modules/compile/compile-node.js
@@ -130,17 +130,17 @@ const compileNode = overload((targets, node) => toType(node), {
return targets;
},
- 'text': (targets, node, path, options, template) => {
+ 'text': (targets, node, path, options, debug) => {
const string = node.nodeValue;
if (!isLiteralString(string)) return targets;
const source = decode(string);
const target = {
- template,
path,
name: indexOf(node),
source,
- Renderer: TextRenderer
+ Renderer: TextRenderer,
+ debug
};
if (window.DEBUG) {
diff --git a/modules/renderer/renderer-text.js b/modules/renderer/renderer-text.js
index b5bb038..6762f3c 100644
--- a/modules/renderer/renderer-text.js
+++ b/modules/renderer/renderer-text.js
@@ -11,7 +11,7 @@ import Data from '../../../fn/modules/data.js';
import { isCommentNode, isElementNode, isFragmentNode, isTextNode } from '../../../dom/modules/node.js';
import include from '../scope/include.js';
import deleteRange from '../dom/delete-range.js';
-import Template from '../template.js';
+import DOMRenderer from '../template.js';
import print, { printError } from '../scope/print.js';
import toText from './to-text.js';
import Renderer, { stats } from './renderer.js';
@@ -55,7 +55,7 @@ function objectToContents(state, object, i) {
// If object is not a node or renderer, append to string. Array.isArray()
// does return true for a proxy of an array.
- if (!(object instanceof Template) && !(object instanceof Node) && !Array.isArray(object)) {
+ if (!(object instanceof DOMRenderer) && !(object instanceof Node) && !Array.isArray(object)) {
state.string += toText(object);
return i;
}
@@ -92,7 +92,7 @@ function objectToContents(state, object, i) {
}
// Object is a freshly rendered Literal Template
- if (object instanceof Template) {
+ if (object instanceof DOMRenderer) {
contents[++i].before(toContent(object));
if (window.DEBUG) ++stats.add;
contents.splice(i, 0, object);
diff --git a/modules/scope/include.js b/modules/scope/include.js
index 24427e4..2942ed0 100644
--- a/modules/scope/include.js
+++ b/modules/scope/include.js
@@ -20,18 +20,19 @@ ${ data.array.map(include('#list-item')) }
import Data from '../../../fn/modules/data.js';
import getById from '../dom/get-by-id.js';
-import Template from '../template.js';
+import Literal from '../template.js';
import requestTemplate from '../request-template.js';
import requestData from '../request-data.js';
-function pipe(template, data, element, consts, options) {
- const renderer = new Template(template, element, consts, options);
+function pipe(template, data, element, consts) {
+ const renderer = Literal.fromTemplate(template, element, consts, data);
+ //const renderer = new Template(template, element, consts, options);
data.each((data) => renderer.push(data));
renderer.done(data);
return renderer;
}
-export default function include(src, data, element, consts, options) {
+export default function include(src, data, element, consts) {
// Operate on target to be sure we are not registering gets in
// parent renderer's signal
const object = Data.objectOf(data);
@@ -45,7 +46,7 @@ export default function include(src, data, element, consts, options) {
// Support JSON or module URLs
if (dataRequest) {
- return dataRequest.then((data) => new Template(template, element, consts, data, options));
+ return dataRequest.then((data) => Literal.fromTemplate(template, element, consts, data));
}
// Support a stream of data
@@ -54,7 +55,7 @@ export default function include(src, data, element, consts, options) {
}
// Support object or ... ?
- return new Template(template, element, consts, object, options);
+ return Literal.fromTemplate(template, element, consts, data);
}
// Template is external to document
@@ -72,7 +73,7 @@ export default function include(src, data, element, consts, options) {
return Promise
.all([templateRequest, dataRequest])
- .then(([template, data]) => new Template(template, element, consts, data, options));
+ .then(([template, data]) => Literal.fromTemplate(template, element, consts, data));
}
diff --git a/modules/template.js b/modules/template.js
index 63233aa..4d97ae4 100644
--- a/modules/template.js
+++ b/modules/template.js
@@ -83,49 +83,88 @@ function removeRange(first, last, fragment) {
fragment.appendChild(dom);
}
-export class LiteralDOM {
+export default class LiteralDOM {
+ static compile(fragment, options, src) {
+ if (window.DEBUG) {
+ groupCollapsed('compile', src, 'yellow');
+ const targets = compileNode(fragment, options, src);
+ groupEnd();
+ return targets;
+ }
+
+ return compileNode(fragment, options);
+ }
+
+ static isTemplate(object) {
+ return object instanceof LiteralRenderer;
+ }
+
+ static of(html) {
+ return LiteralRenderer.from(create('template', html));
+ }
+
+ static fromFragment(fragment, identifier, element, consts = {}, data, options) {
+ const compiled = cache[identifier] || (
+ cache[identifier] = LiteralDOM.compile(fragment, options, identifier)
+ );
+
+ // fragment, targets, element, consts, data, options
+ return new LiteralDOM(fragment.cloneNode(true), compiled, element, consts, data, options);
+ }
+
+ static fromTemplate(template, element, consts = {}, data) {
+ const id = identify(template, 'literal-');
+ const fragment = template.content;
+
+ const options = {
+ nostrict: template.hasAttribute && template.hasAttribute('nostrict')
+ };
+
+ return LiteralDOM.fromFragment(fragment, '#' + id, element, consts, data, options);
+ }
+
#first;
#last;
#data;
- constructor(content, targets, parent = template.parentElement, consts = {}, data, options = defaults) {
- const children = content.childNodes;
+ // fragment, targets, element, consts, data, options
+ constructor(fragment, targets, parent = template.parentElement, consts = {}, data, options = defaults) {
+ const children = fragment.childNodes;
// The first node may change. The last node is always the last node.
- this.#data = Signal.of(Data.objectOf(data));
- this.#first = children[0];
- this.#last = children[children.length - 1];
-
- this.content = content;
- this.element = parent;
- this.consts = consts;
- this.contents = targets
+ this.#data = Signal.of(Data.objectOf(data));
+ this.#first = children[0];
+ this.#last = children[children.length - 1];
+ this.content = fragment;
+ this.element = parent;
+ this.consts = consts;
+ this.contents = targets
// We must find targets in cloned content
- .map(this.#toRendererParams, this)
+ .map(this.#toCompiled, this)
// before we create renderers for them, as renderers may mutate the DOM
.map(this.#toRenderer, this);
}
- #toRendererParams(target) {
- const { path, name, literal, message, template } = target;
+ #toCompiled(compiled) {
+ const { path, name } = compiled;
// Where `.path` exists find the element at the end of the path
const element = path ? getElement(path, this.content) : this.element ;
// Text renderer expects a text node that must always come from the
// cloned content fragment
- const n = typeof name === 'number' ?
+ const node = typeof name === 'number' ?
path ? element.childNodes[name] :
this.content.childNodes[name] :
name;
- // Parameters for Renderer.create():
- // signal, literal, consts, element, nameOrNode
- return [this.#data, literal, this.consts, element, n, target];
+ // Parameters for new Renderer()
+ return { element, node, compiled };
}
- #toRenderer(parameters) {
- const renderer = Renderer.create(...parameters);
+ #toRenderer({ element, node, compiled }) {
+ const { Renderer, literal } = compiled;
+ const renderer = new Renderer(this.#data, literal, this.consts, element, node, compiled);
this.done(renderer);
return renderer;
}
@@ -255,49 +294,3 @@ assign(LiteralDOM.prototype, {
done: Renderer.prototype.done
});
-
-export default class LiteralRenderer extends LiteralDOM {
- static isTemplate(object) {
- return object instanceof LiteralRenderer;
- }
-
- static of(html) {
- return LiteralRenderer.from(create('template', html));
- }
-
- static from(template, parent) {
- const id = identify(template, 'literal-');
- const fragment = template.content;
- const compiled = cache[id]
- || (cache[id] = LiteralRenderer.compile(fragment, options, '#' + id));
-
- return new LiteralDOM(compiled, fragment.cloneNode(true), parent = template.parentElement);
- }
-
- static compile(fragment, options, src) {
- let targets;
-
- if (window.DEBUG) {
- groupCollapsed('compile', src, 'yellow');
- targets = compileNode(fragment, options, src);
- groupEnd();
- }
- else {
- targets = compileNode(fragment, options);
- }
-
- return targets;
- }
-
- constructor(template, parent = template.parentElement, consts = {}, data, o = defaults) {
- const id = identify(template, 'literal-');
- const options = assign({}, o, {
- nostrict: template.hasAttribute && template.hasAttribute('nostrict')
- });
-
- const compiled = cache[id]
- || (cache[id] = LiteralRenderer.compile(template.content, options, '#' + id));
-
- super(template.content.cloneNode(true), compiled, parent, consts, data, options);
- }
-}