straylight
Advanced tools
| import * as dom from './dom.js'; | ||
| import { | ||
| CommentUpdater, | ||
| AttributeUpdater, | ||
| AttributePartUpdater, | ||
| PropertyMapUpdater, | ||
| ChildUpdater } from './updaters.js'; | ||
| class Builder { | ||
| constructor() { | ||
| this.updaters = null; | ||
| } | ||
| build(template, parent, next) { | ||
| this.updaters = []; | ||
| this.buildNode(template, parent, next); | ||
| return this.updaters; | ||
| } | ||
| addAttributePartUpdater(element, name, parts) { | ||
| let updaterParts = [parts[0]]; | ||
| updaterParts.pending = []; | ||
| for (let i = 1; i < parts.length; ++i) { | ||
| let pos = updaterParts.length; | ||
| updaterParts.push(''); | ||
| updaterParts.push(parts[i]); | ||
| let updater = new AttributePartUpdater(element, name, updaterParts, pos); | ||
| this.updaters.push(updater); | ||
| } | ||
| } | ||
| buildNode(node, parent, next) { | ||
| switch (node.kind) { | ||
| case 'root': | ||
| this.buildChildren(node, parent, next); | ||
| return; | ||
| case 'child-slot': | ||
| this.updaters.push(new ChildUpdater(parent, next)); | ||
| return; | ||
| case 'null-slot': | ||
| this.updaters.push(new CommentUpdater()); | ||
| return; | ||
| } | ||
| let elem = dom.createElement(node.tag, parent); | ||
| for (let attr of node.attributes) { | ||
| switch (attr.kind) { | ||
| case 'static': | ||
| dom.setAttr(elem, attr.name, attr.value); | ||
| break; | ||
| case 'value': | ||
| this.updaters.push(new AttributeUpdater(elem, attr.name)); | ||
| break; | ||
| case 'map': | ||
| this.updaters.push(new PropertyMapUpdater(elem)); | ||
| break; | ||
| case 'null': | ||
| this.updaters.push(new CommentUpdater()); | ||
| break; | ||
| case 'parts': | ||
| this.addAttributePartUpdater(elem, attr.name, attr.value); | ||
| break; | ||
| } | ||
| } | ||
| this.buildChildren(node, elem, null); | ||
| dom.insertChild(elem, parent, next); | ||
| } | ||
| buildChildren(node, parent, next) { | ||
| for (let child of node.children) { | ||
| if (typeof child === 'string') { | ||
| let text = dom.createText(child, parent); | ||
| dom.insertChild(text, parent, next); | ||
| } else { | ||
| this.buildNode(child, parent, next); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| export function buildTemplate(template, parent, next) { | ||
| return new Builder().build(template, parent, next); | ||
| } |
+226
-291
@@ -6,14 +6,2 @@ function makeEnum(i = 0) { | ||
| const { | ||
| T_NONE, | ||
| T_COMMENT, | ||
| T_TEXT, | ||
| T_ATTR_PART, | ||
| T_ATTR_MAP, | ||
| T_ATTR_VALUE, | ||
| T_TAG_START, | ||
| T_ATTR_KEY, | ||
| T_TAG_END, | ||
| } = makeEnum(); | ||
| const { | ||
| S_TEXT, | ||
@@ -23,2 +11,3 @@ S_RAW, | ||
| S_ATTR, | ||
| S_ATTR_CLOSE, | ||
| S_ATTR_KEY, | ||
@@ -33,9 +22,8 @@ S_ATTR_KEY_WS, | ||
| const $tokens = Symbol('tokens'); | ||
| const ESC_RE = /&(?:(lt|gt|amp|quot)|#([0-9]+)|#x([0-9a-f]+));?/ig; | ||
| const ESC_RE = /&(?:(lt|gt|amp|quot)|#([0-9]+)|#x([0-9a-f]+));?/ig; | ||
| const NAMED_REFS = { lt: '<', gt: '>', amp: '&', quot: '"' }; | ||
| function wsToken(t) { | ||
| return t.type === T_TEXT && !t.mutable && (!t.value || !t.value.trim()); | ||
| function notReached(msg) { | ||
| throw new Error(msg); | ||
| } | ||
@@ -74,22 +62,47 @@ | ||
| class Token { | ||
| constructor(type, value, hasEscape) { | ||
| this.type = type; | ||
| this.value = hasEscape ? escape(value) : value; | ||
| this.mutable = false; | ||
| function maybeEscape(hasEscape, text) { | ||
| return hasEscape ? escape(text) : text; | ||
| } | ||
| class ParseNode { | ||
| constructor(kind) { | ||
| this.kind = kind; | ||
| this.tag = ''; | ||
| this.attributes = []; | ||
| this.children = []; | ||
| } | ||
| } | ||
| class Attribute { | ||
| constructor(kind, name, value) { | ||
| this.kind = kind; | ||
| this.name = name; | ||
| this.value = value; | ||
| } | ||
| } | ||
| class Parser { | ||
| constructor() { | ||
| this.tokens = []; | ||
| this.node = new ParseNode('root'); | ||
| this.stack = [this.node]; | ||
| this.state = S_TEXT; | ||
| this.attrName = ''; | ||
| this.attrParts = []; | ||
| this.tag = ''; | ||
| this.closing = false; | ||
| } | ||
| addAttr(value) { | ||
| this.node.attributes.push(new Attribute('static', this.attrName, value)); | ||
| } | ||
| addAttrParts() { | ||
| let attr = new Attribute('parts', this.attrName, this.attrParts); | ||
| this.node.attributes.push(attr); | ||
| this.attrParts = []; | ||
| } | ||
| parseChunk(chunk) { | ||
| let state = this.state; | ||
| let tokens = this.tokens; | ||
| let attrPart = (state === S_ATTR_VALUE_DQ || state === S_ATTR_VALUE_SQ); | ||
| let attrPart = state === S_ATTR_VALUE_DQ || state === S_ATTR_VALUE_SQ; | ||
| let hasEscape = false; | ||
@@ -108,5 +121,3 @@ let a = 0; | ||
| if (c === '>' && rmatch(chunk, b, '--')) { | ||
| if (b - 2 > a) { | ||
| tokens.push(new Token(T_COMMENT, chunk.slice(a, b - 2), false)); | ||
| } | ||
| // Comments are ignored | ||
| state = S_TEXT; | ||
@@ -121,6 +132,8 @@ hasEscape = false; | ||
| if (b > a) { | ||
| tokens.push(new Token(T_TEXT, chunk.slice(a, b), hasEscape)); | ||
| let text = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| this.node.children.push(text); | ||
| } | ||
| state = S_OPEN; | ||
| hasEscape = false; | ||
| this.closing = false; | ||
| a = b + 1; | ||
@@ -130,7 +143,13 @@ } | ||
| if (c === "'") { | ||
| let type = attrPart ? T_ATTR_PART : T_ATTR_VALUE; | ||
| tokens.push(new Token(type, chunk.slice(a, b), hasEscape)); | ||
| let value = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| if (attrPart) { | ||
| this.attrParts.push(value); | ||
| this.addAttrParts(); | ||
| attrPart = false; | ||
| } else { | ||
| this.addAttr(value); | ||
| } | ||
| state = S_ATTR; | ||
| hasEscape = false; | ||
| attrPart = false; | ||
| this.attrName = ''; | ||
| a = b + 1; | ||
@@ -140,7 +159,13 @@ } | ||
| if (c === '"') { | ||
| let type = attrPart ? T_ATTR_PART : T_ATTR_VALUE; | ||
| tokens.push(new Token(type, chunk.slice(a, b), hasEscape)); | ||
| let value = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| if (attrPart) { | ||
| this.attrParts.push(value); | ||
| this.addAttrParts(); | ||
| attrPart = false; | ||
| } else { | ||
| this.addAttr(value); | ||
| } | ||
| state = S_ATTR; | ||
| hasEscape = false; | ||
| attrPart = false; | ||
| this.attrName = ''; | ||
| a = b + 1; | ||
@@ -150,27 +175,25 @@ } | ||
| if (state === S_OPEN) { | ||
| let value = chunk.slice(a, b); | ||
| tokens.push(new Token(T_TAG_START, value, hasEscape)); | ||
| hasEscape = false; | ||
| a = b; | ||
| this.tag = value; | ||
| this.tag = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| if (!this.closing) { | ||
| this.pushNode(); | ||
| } | ||
| } else if (state === S_ATTR_KEY) { | ||
| tokens.push(new Token(T_ATTR_KEY, chunk.slice(a, b), hasEscape)); | ||
| hasEscape = false; | ||
| a = b; | ||
| this.attrName = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| this.addAttr(true); | ||
| } else if (state === S_ATTR_KEY_WS) { | ||
| this.addAttr(true); | ||
| } else if (state === S_ATTR_VALUE) { | ||
| tokens.push(new Token(T_ATTR_VALUE, chunk.slice(a, b), hasEscape)); | ||
| hasEscape = false; | ||
| a = b; | ||
| this.addAttr(maybeEscape(hasEscape, chunk.slice(a, b))); | ||
| } else if (state === S_ATTR_VALUE_WS) { | ||
| this.addAttr(true); | ||
| } | ||
| if (rmatch(chunk, b, '/') && this.tag[0] !== '/') { | ||
| tokens.push(new Token(T_TAG_END, '/', false)); | ||
| if (this.closing || rmatch(chunk, b, '/')) { | ||
| // Closing or self-closing tag | ||
| this.popNode(); | ||
| state = S_TEXT; | ||
| hasEscape = false; | ||
| a = b + 1; | ||
| } else { | ||
| tokens.push(new Token(T_TAG_END, '', false)); | ||
| hasEscape = false; | ||
| state = rawTag(this.tag) ? S_RAW : S_TEXT; | ||
| a = b + 1; | ||
| } | ||
| hasEscape = false; | ||
| a = b + 1; | ||
| } else if (state === S_OPEN) { | ||
@@ -180,11 +203,16 @@ if (c === '-' && chunk.slice(a, b) === '!-') { | ||
| a = b + 1; | ||
| } else if (c === '/' && b === a) ; else if (!attrChar(c)) { | ||
| let value = chunk.slice(a, b); | ||
| tokens.push(new Token(T_TAG_START, value, hasEscape)); | ||
| this.tag = value; | ||
| } else if (c === '/' && b === a) { | ||
| this.closing = true; | ||
| } else if (!attrChar(c)) { | ||
| this.tag = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| if (this.closing) { | ||
| state = S_ATTR_CLOSE; | ||
| } else { | ||
| this.pushNode(); | ||
| state = S_ATTR; | ||
| } | ||
| hasEscape = false; | ||
| state = S_ATTR; | ||
| a = b + 1; | ||
| } | ||
| } else if (state === S_ATTR) { | ||
| } else if (state === S_ATTR_CLOSE) ; else if (state === S_ATTR) { | ||
| if (attrChar(c)) { | ||
@@ -196,3 +224,3 @@ state = S_ATTR_KEY; | ||
| if (c === '=') { | ||
| tokens.push(new Token(T_ATTR_KEY, chunk.slice(a, b), hasEscape)); | ||
| this.attrName = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| hasEscape = false; | ||
@@ -202,3 +230,3 @@ state = S_ATTR_VALUE_WS; | ||
| } else if (!attrChar(c)) { | ||
| tokens.push(new Token(T_ATTR_KEY, chunk.slice(a, b), hasEscape)); | ||
| this.attrName = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| hasEscape = false; | ||
@@ -213,2 +241,3 @@ state = S_ATTR_KEY_WS; | ||
| } else if (attrChar(c)) { | ||
| this.addAttr(true); | ||
| state = S_ATTR_KEY; | ||
@@ -230,3 +259,3 @@ a = b; | ||
| if (!attrChar(c)) { | ||
| tokens.push(new Token(T_ATTR_VALUE, chunk.slice(a, b), hasEscape)); | ||
| this.addAttr(maybeEscape(hasEscape, chunk.slice(a, b))); | ||
| hasEscape = false; | ||
@@ -241,17 +270,15 @@ state = S_ATTR; | ||
| if (a < b) { | ||
| tokens.push(new Token(T_TEXT, chunk.slice(a, b), hasEscape)); | ||
| let text = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| this.node.children.push(text); | ||
| } | ||
| } else if (state === S_COMMENT) { | ||
| if (a < b) { | ||
| tokens.push(new Token(T_COMMENT, chunk.slice(a, b), hasEscape)); | ||
| } | ||
| } else if (state === S_OPEN) { | ||
| if (a < b) { | ||
| let value = chunk.slice(a, b); | ||
| tokens.push(new Token(T_TAG_START, value, hasEscape)); | ||
| this.tag = value; | ||
| } else if (state === S_COMMENT) ; else if (state === S_OPEN) { | ||
| this.tag = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| if (this.closing) { | ||
| state = S_ATTR_CLOSE; | ||
| } else { | ||
| this.pushNode(); | ||
| state = S_ATTR; | ||
| } | ||
| } else if (state === S_ATTR_KEY) { | ||
| tokens.push(new Token(T_ATTR_KEY, chunk.slice(a, b), hasEscape)); | ||
| this.attrName = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| state = S_ATTR; | ||
@@ -261,8 +288,7 @@ } else if (state === S_ATTR_KEY_WS) { | ||
| } else if (state === S_ATTR_VALUE) { | ||
| tokens.push(new Token(T_ATTR_VALUE, chunk.slice(a, b), hasEscape)); | ||
| this.addAttr(maybeEscape(hasEscape, chunk.slice(a, b))); | ||
| state = S_ATTR; | ||
| } else if (state === S_ATTR_VALUE_SQ || state === S_ATTR_VALUE_DQ) { | ||
| if (a < b) { | ||
| tokens.push(new Token(T_ATTR_PART, chunk.slice(a, b), hasEscape)); | ||
| } | ||
| let value = maybeEscape(hasEscape, chunk.slice(a, b)); | ||
| this.attrParts.push(value); | ||
| } | ||
@@ -273,23 +299,34 @@ | ||
| pushValue(value) { | ||
| let type = T_NONE; | ||
| pushNode() { | ||
| let node = new ParseNode('element'); | ||
| node.tag = this.tag; | ||
| this.node.children.push(node); | ||
| this.node = node; | ||
| this.stack.push(node); | ||
| } | ||
| popNode() { | ||
| if (this.stack.length > 1) { | ||
| this.stack.pop(); | ||
| this.node = this.stack[this.stack.length - 1]; | ||
| } | ||
| } | ||
| addSlot() { | ||
| switch (this.state) { | ||
| case S_TEXT: | ||
| case S_RAW: | ||
| type = T_TEXT; | ||
| this.node.children.push(new ParseNode('child-slot')); | ||
| break; | ||
| case S_COMMENT: | ||
| type = T_COMMENT; | ||
| this.node.children.push(new ParseNode('null-slot')); | ||
| break; | ||
| case S_OPEN: | ||
| type = T_TAG_START; | ||
| this.tag = value; | ||
| this.state = S_ATTR; | ||
| break; | ||
| case S_ATTR: | ||
| type = T_ATTR_MAP; | ||
| this.node.attributes.push(new Attribute('map', '', '')); | ||
| break; | ||
| case S_ATTR_CLOSE: | ||
| this.node.attributes.push(new Attribute('null', '', '')); | ||
| break; | ||
| case S_ATTR_VALUE_WS: | ||
| type = T_ATTR_VALUE; | ||
| this.node.attributes.push(new Attribute('value', this.attrName, '')); | ||
| this.state = S_ATTR; | ||
@@ -299,39 +336,25 @@ break; | ||
| case S_ATTR_VALUE_DQ: | ||
| type = T_ATTR_PART; | ||
| // Slots will be added when the next chunk is parsed | ||
| break; | ||
| default: | ||
| notReached('Unexpected parser state'); | ||
| break; | ||
| } | ||
| if (type !== T_NONE) { | ||
| let token = new Token(type, value); | ||
| token.mutable = true; | ||
| this.tokens.push(token); | ||
| } | ||
| } | ||
| end() { | ||
| let tokens = this.tokens; | ||
| let a = 0; | ||
| let b = tokens.length; | ||
| if (b === 0) { | ||
| return tokens; | ||
| } | ||
| if (wsToken(tokens[0])) { a++; } | ||
| if (wsToken(tokens[b - 1])) { b--; } | ||
| return a === 0 && b === tokens.length ? tokens : tokens.slice(a, b); | ||
| if (this.attrParts.length > 0) { this.addAttrParts(); } | ||
| while (this.stack.length > 1) { this.popNode(); } | ||
| return this.node; | ||
| } | ||
| } | ||
| function tokenize(chunks) { | ||
| if (chunks.length === 0) { | ||
| return []; | ||
| } | ||
| function parse(chunks) { | ||
| let parser = new Parser(); | ||
| parser.parseChunk(chunks[0]); | ||
| for (let i = 1; i < chunks.length; i++) { | ||
| parser.pushValue(''); | ||
| parser.parseChunk(chunks[i]); | ||
| if (chunks.length > 0) { | ||
| parser.parseChunk(chunks[0]); | ||
| for (let i = 1; i < chunks.length; i++) { | ||
| parser.addSlot(); | ||
| parser.parseChunk(chunks[i]); | ||
| } | ||
| } | ||
@@ -341,88 +364,7 @@ return parser.end(); | ||
| function walk(i, node, tokens, vals, actions) { | ||
| for (; i < tokens.length; ++i) { | ||
| let t = tokens[i]; | ||
| switch (t.type) { | ||
| case T_TAG_START: { | ||
| let tag = vals.read(t); | ||
| if (typeof tag === 'string' && tag[0] === '/') { // Closing tag | ||
| while (i < tokens.length && tokens[++i].type !== T_TAG_END); // Skip attributes | ||
| return i; | ||
| } | ||
| let child = actions.createElement(tag, node); | ||
| i = walk(i + 1, child, tokens, vals, actions); | ||
| actions.appendChild(node, actions.finishElement(child)); | ||
| break; | ||
| } | ||
| case T_TAG_END: | ||
| if (t.value === '/') { return i; } | ||
| break; | ||
| case T_TEXT: | ||
| actions.appendChild(node, vals.read(t)); | ||
| break; | ||
| case T_COMMENT: | ||
| actions.appendChild(node, actions.createComment(vals.read(t), node)); | ||
| break; | ||
| case T_ATTR_MAP: | ||
| actions.setAttributes(node, vals.read(t)); | ||
| break; | ||
| case T_ATTR_KEY: { | ||
| let name = vals.read(t); | ||
| switch (i + 1 < tokens.length ? tokens[i + 1].type : T_NONE) { | ||
| case T_ATTR_VALUE: | ||
| actions.setAttribute(node, name, vals.read(tokens[++i])); | ||
| break; | ||
| case T_ATTR_PART: { | ||
| let parts = [vals.read(tokens[++i])]; | ||
| while (i + 1 < tokens.length && tokens[i + 1].type === T_ATTR_PART) { | ||
| parts.push(vals.read(tokens[++i])); | ||
| } | ||
| actions.setAttributeParts(node, name, parts); | ||
| break; | ||
| } | ||
| default: | ||
| actions.setAttribute(node, name, true); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| class Vals { | ||
| constructor(values, actions) { | ||
| this.index = 0; | ||
| this.values = values; | ||
| this.actions = actions; | ||
| } | ||
| read(t) { | ||
| return t.mutable | ||
| ? this.actions.mapValue(this.values[this.index++]) | ||
| : t.value; | ||
| } | ||
| } | ||
| class TemplateResult { | ||
| constructor(callsite, values) { | ||
| let tokens = TemplateResult.cache.get(callsite); | ||
| if (!tokens) { | ||
| tokens = tokenize(callsite.raw); | ||
| tokens.source = {}; | ||
| TemplateResult.cache.set(callsite, tokens); | ||
| } | ||
| this[$tokens] = tokens; | ||
| this.source = tokens.source; | ||
| constructor(template, values) { | ||
| this.template = template; | ||
| this.values = values; | ||
| } | ||
| evaluate(actions) { | ||
| let root = actions.createRoot(); | ||
| walk(0, root, this[$tokens], new Vals(this.values, actions), actions); | ||
| return actions.finishRoot(root); | ||
| } | ||
| } | ||
@@ -432,11 +374,9 @@ | ||
| // IE11's WeakMap implementation is incorrect | ||
| try { | ||
| new WeakMap().set({}, 1).get({}); | ||
| } catch (e) { | ||
| TemplateResult.cache = new Map(); | ||
| } | ||
| function html(callsite, ...values) { | ||
| return new TemplateResult(callsite, values); | ||
| let template = TemplateResult.cache.get(callsite); | ||
| if (!template) { | ||
| template = parse(callsite.raw); | ||
| TemplateResult.cache.set(callsite, template); | ||
| } | ||
| return new TemplateResult(template, values); | ||
| } | ||
@@ -619,4 +559,4 @@ | ||
| class ChildUpdater { | ||
| constructor(context, parent, next) { | ||
| this.slot = createSlot(context, parent, next, null); | ||
| constructor(parent, next) { | ||
| this.slot = createSlot(parent, next, null); | ||
| } | ||
@@ -629,77 +569,71 @@ | ||
| class Dynamic { | ||
| constructor(value) { this.value = value; } | ||
| } | ||
| class Builder { | ||
| constructor() { | ||
| this.updaters = null; | ||
| } | ||
| function isDynamic(x) { | ||
| return x instanceof Dynamic; | ||
| } | ||
| class Actions { | ||
| constructor(context, root, next) { | ||
| this.context = context; | ||
| this.root = root; | ||
| this.next = next; | ||
| build(template, parent, next) { | ||
| this.updaters = []; | ||
| } | ||
| createRoot() { | ||
| return this.root; | ||
| } | ||
| finishRoot() { | ||
| this.buildNode(template, parent, next); | ||
| return this.updaters; | ||
| } | ||
| createElement(tag, parent) { | ||
| if (typeof tag !== 'string') { | ||
| throw new TypeError('Tag name must be a string'); | ||
| addAttributePartUpdater(element, name, parts) { | ||
| let updaterParts = [parts[0]]; | ||
| updaterParts.pending = []; | ||
| for (let i = 1; i < parts.length; ++i) { | ||
| let pos = updaterParts.length; | ||
| updaterParts.push(''); | ||
| updaterParts.push(parts[i]); | ||
| let updater = new AttributePartUpdater(element, name, updaterParts, pos); | ||
| this.updaters.push(updater); | ||
| } | ||
| return createElement(tag, parent); | ||
| } | ||
| createComment(value) { | ||
| if (isDynamic(value)) { | ||
| this.updaters.push(new CommentUpdater()); | ||
| buildNode(node, parent, next) { | ||
| switch (node.kind) { | ||
| case 'root': | ||
| this.buildChildren(node, parent, next); | ||
| return; | ||
| case 'child-slot': | ||
| this.updaters.push(new ChildUpdater(parent, next)); | ||
| return; | ||
| case 'null-slot': | ||
| this.updaters.push(new CommentUpdater()); | ||
| return; | ||
| } | ||
| return null; | ||
| } | ||
| finishElement(node) { | ||
| return node; | ||
| } | ||
| let elem = createElement(node.tag, parent); | ||
| appendChild(node, child) { | ||
| let next = node === this.root ? this.next : null; | ||
| if (isDynamic(child)) { | ||
| this.updaters.push(new ChildUpdater(this.context, node, next)); | ||
| } else if (child !== null) { | ||
| if (typeof child === 'string') { | ||
| child = createText(child, node); | ||
| for (let attr of node.attributes) { | ||
| switch (attr.kind) { | ||
| case 'static': | ||
| setAttr(elem, attr.name, attr.value); | ||
| break; | ||
| case 'value': | ||
| this.updaters.push(new AttributeUpdater(elem, attr.name)); | ||
| break; | ||
| case 'map': | ||
| this.updaters.push(new PropertyMapUpdater(elem)); | ||
| break; | ||
| case 'null': | ||
| this.updaters.push(new CommentUpdater()); | ||
| break; | ||
| case 'parts': | ||
| this.addAttributePartUpdater(elem, attr.name, attr.value); | ||
| break; | ||
| } | ||
| insertChild(child, node, next); | ||
| } | ||
| } | ||
| mapValue(v) { | ||
| return new Dynamic(v); | ||
| this.buildChildren(node, elem, null); | ||
| insertChild(elem, parent, next); | ||
| } | ||
| setAttribute(node, name, value) { | ||
| if (isDynamic(value)) { | ||
| this.updaters.push(new AttributeUpdater(node, name)); | ||
| } else { | ||
| setAttr(node, name, value); | ||
| } | ||
| } | ||
| setAttributes(node) { | ||
| this.updaters.push(new PropertyMapUpdater(node)); | ||
| } | ||
| setAttributeParts(node, name, parts) { | ||
| parts.pending = []; | ||
| for (let i = 0; i < parts.length; ++i) { | ||
| if (isDynamic(parts[i])) { | ||
| this.updaters.push(new AttributePartUpdater(node, name, parts, i)); | ||
| buildChildren(node, parent, next) { | ||
| for (let child of node.children) { | ||
| if (typeof child === 'string') { | ||
| let text = createText(child, parent); | ||
| insertChild(text, parent, next); | ||
| } else { | ||
| this.buildNode(child, parent, next); | ||
| } | ||
@@ -710,19 +644,23 @@ } | ||
| function buildTemplate(template, parent, next) { | ||
| return new Builder().build(template, parent, next); | ||
| } | ||
| const createSlotSymbol = Symbol('createSlot'); | ||
| function createSlot(context, parent, next, value) { | ||
| function createSlot(parent, next, value) { | ||
| if (isTextLike(value)) { | ||
| return new TextSlot(context, parent, next, value); | ||
| return new TextSlot(parent, next, value); | ||
| } | ||
| if (value instanceof TemplateResult) { | ||
| return new TemplateSlot(context, parent, next, value); | ||
| return new TemplateSlot(parent, next, value); | ||
| } | ||
| if (typeof value[createSlotSymbol] === 'function') { | ||
| return value[createSlotSymbol](context, parent, next); | ||
| return value[createSlotSymbol](parent, next); | ||
| } | ||
| if (isIterable(value)) { | ||
| return new MapSlot(context, parent, next, value); | ||
| return new MapSlot(parent, next, value); | ||
| } | ||
@@ -739,3 +677,3 @@ | ||
| let next = slot.start; | ||
| let newSlot = createSlot(slot.context, parent(next), next, value); | ||
| let newSlot = createSlot(parent(next), next, value); | ||
| removeSlot(slot); | ||
@@ -769,6 +707,5 @@ return newSlot; | ||
| class TextSlot { | ||
| constructor(context, parent, next, value) { | ||
| constructor(parent, next, value) { | ||
| let node = createText(value, parent); | ||
| insertChild(node, parent, next); | ||
| this.context = context; | ||
| this.start = node; | ||
@@ -850,7 +787,6 @@ this.end = node; | ||
| class MapSlot { | ||
| constructor(context, parent, next, value) { | ||
| this.context = context; | ||
| constructor(parent, next, value) { | ||
| this.parent = parent; | ||
| this.map = new Map(); | ||
| this.list = new MapSlotList(createSlot(this.context, parent, next), null); | ||
| this.list = new MapSlotList(createSlot(parent, next), null); | ||
| this.update(value); | ||
@@ -902,3 +838,3 @@ } | ||
| } else { | ||
| let slot = createSlot(this.context, this.parent, next.slot.start, value); | ||
| let slot = createSlot(this.parent, next.slot.start, value); | ||
| item = new MapSlotList(slot, key); | ||
@@ -921,4 +857,3 @@ item.insertBefore(next); | ||
| class TemplateSlot { | ||
| constructor(context, parent, next, template) { | ||
| this.context = context; | ||
| constructor(parent, next, value) { | ||
| // The first and last nodes of the template could be dynamic, | ||
@@ -928,6 +863,6 @@ // so create stable marker nodes before and after the content. | ||
| this.end = insertMarker(parent, next); | ||
| this.source = template.source; | ||
| this.updaters = template.evaluate(new Actions(context, parent, this.end)); | ||
| this.template = value.template; | ||
| this.updaters = buildTemplate(this.template, parent, this.end); | ||
| this.pending = Array(this.updaters.length); | ||
| this.update(template); | ||
| this.update(value); | ||
| } | ||
@@ -948,9 +883,9 @@ | ||
| value instanceof TemplateResult && | ||
| value.source === this.source | ||
| value.template === this.template | ||
| ); | ||
| } | ||
| update(template) { | ||
| // Assert: template.source === this.updater.source | ||
| let { values } = template; | ||
| update(value) { | ||
| // Assert: value.template === this.template | ||
| let { values } = value; | ||
| for (let i = 0; i < this.updaters.length; ++i) { | ||
@@ -1047,3 +982,3 @@ let value = values[i]; | ||
| let next = firstChild(target); | ||
| slot = createSlot(null, target, next, template); | ||
| slot = createSlot(target, next, template); | ||
| if (next) { | ||
@@ -1056,2 +991,2 @@ removeSiblings(next, null); | ||
| export { applyTemplate, createSlot, createSlotSymbol, html, removeSlot, updateSlot, withKey }; | ||
| export { TemplateResult, applyTemplate, createSlot, createSlotSymbol, html, removeSlot, updateSlot, withKey }; |
@@ -1,1 +0,1 @@ | ||
| function makeEnum(t=0){return new Proxy({},{get:()=>t++})}const{T_NONE:T_NONE,T_COMMENT:T_COMMENT,T_TEXT:T_TEXT,T_ATTR_PART:T_ATTR_PART,T_ATTR_MAP:T_ATTR_MAP,T_ATTR_VALUE:T_ATTR_VALUE,T_TAG_START:T_TAG_START,T_ATTR_KEY:T_ATTR_KEY,T_TAG_END:T_TAG_END}=makeEnum(),{S_TEXT:S_TEXT,S_RAW:S_RAW,S_OPEN:S_OPEN,S_ATTR:S_ATTR,S_ATTR_KEY:S_ATTR_KEY,S_ATTR_KEY_WS:S_ATTR_KEY_WS,S_ATTR_VALUE_WS:S_ATTR_VALUE_WS,S_ATTR_VALUE:S_ATTR_VALUE,S_ATTR_VALUE_SQ:S_ATTR_VALUE_SQ,S_ATTR_VALUE_DQ:S_ATTR_VALUE_DQ,S_COMMENT:S_COMMENT}=makeEnum(),$tokens=Symbol("tokens"),ESC_RE=/&(?:(lt|gt|amp|quot)|#([0-9]+)|#x([0-9a-f]+));?/gi,NAMED_REFS={lt:"<",gt:">",amp:"&",quot:'"'};function wsToken(t){return!(t.type!==T_TEXT||t.mutable||t.value&&t.value.trim())}function rmatch(t,e,s){return e>=s.length&&t.slice(e-s.length,e)===s}function wsChar(t){switch(t){case" ":case"\n":case"\r":case"\t":case"\f":case"\v":return!0}return t.charCodeAt(0)>128&&/\s/.test(t)}function attrChar(t){return!wsChar(t)&&'"'!==t&&"'"!==t&&"="!==t&&"/"!==t}function rawTag(t){return"script"===t||"style"===t}function escape(t){return t.replace(ESC_RE,((t,e,s,n)=>e?NAMED_REFS[e.toLowerCase()]:String.fromCharCode(parseInt(s||n,n?16:10))))}class Token{constructor(t,e,s){this.type=t,this.value=s?escape(e):e,this.mutable=!1}}class Parser{constructor(){this.tokens=[],this.state=S_TEXT,this.tag=""}parseChunk(t){let e=this.state,s=this.tokens,n=e===S_ATTR_VALUE_DQ||e===S_ATTR_VALUE_SQ,r=!1,i=0,a=0;for(;a<t.length;++a){let o=t[a];if(e===S_RAW)">"===o&&rmatch(t,a,"</"+this.tag)&&(a-=this.tag.length+3,e=S_TEXT);else if(e===S_COMMENT)">"===o&&rmatch(t,a,"--")&&(a-2>i&&s.push(new Token(T_COMMENT,t.slice(i,a-2),!1)),e=S_TEXT,r=!1,i=a+1);else if("&"===o)r=!0;else if(e===S_TEXT)"<"===o&&(a>i&&s.push(new Token(T_TEXT,t.slice(i,a),r)),e=S_OPEN,r=!1,i=a+1);else if(e===S_ATTR_VALUE_SQ){if("'"===o){let o=n?T_ATTR_PART:T_ATTR_VALUE;s.push(new Token(o,t.slice(i,a),r)),e=S_ATTR,r=!1,n=!1,i=a+1}}else if(e===S_ATTR_VALUE_DQ){if('"'===o){let o=n?T_ATTR_PART:T_ATTR_VALUE;s.push(new Token(o,t.slice(i,a),r)),e=S_ATTR,r=!1,n=!1,i=a+1}}else if(">"===o){if(e===S_OPEN){let e=t.slice(i,a);s.push(new Token(T_TAG_START,e,r)),r=!1,i=a,this.tag=e}else e===S_ATTR_KEY?(s.push(new Token(T_ATTR_KEY,t.slice(i,a),r)),r=!1,i=a):e===S_ATTR_VALUE&&(s.push(new Token(T_ATTR_VALUE,t.slice(i,a),r)),r=!1,i=a);rmatch(t,a,"/")&&"/"!==this.tag[0]?(s.push(new Token(T_TAG_END,"/",!1)),e=S_TEXT,r=!1,i=a+1):(s.push(new Token(T_TAG_END,"",!1)),r=!1,e=rawTag(this.tag)?S_RAW:S_TEXT,i=a+1)}else if(e===S_OPEN){if("-"===o&&"!-"===t.slice(i,a))e=S_COMMENT,i=a+1;else if("/"===o&&a===i);else if(!attrChar(o)){let n=t.slice(i,a);s.push(new Token(T_TAG_START,n,r)),this.tag=n,r=!1,e=S_ATTR,i=a+1}}else e===S_ATTR?attrChar(o)&&(e=S_ATTR_KEY,i=a):e===S_ATTR_KEY?"="===o?(s.push(new Token(T_ATTR_KEY,t.slice(i,a),r)),r=!1,e=S_ATTR_VALUE_WS,i=a+1):attrChar(o)||(s.push(new Token(T_ATTR_KEY,t.slice(i,a),r)),r=!1,e=S_ATTR_KEY_WS,i=a+1):e===S_ATTR_KEY_WS?"="===o?(e=S_ATTR_VALUE_WS,i=a+1):attrChar(o)&&(e=S_ATTR_KEY,i=a):e===S_ATTR_VALUE_WS?'"'===o?(e=S_ATTR_VALUE_DQ,i=a+1):"'"===o?(e=S_ATTR_VALUE_SQ,i=a+1):attrChar(o)&&(e=S_ATTR_VALUE,i=a):e===S_ATTR_VALUE&&(attrChar(o)||(s.push(new Token(T_ATTR_VALUE,t.slice(i,a),r)),r=!1,e=S_ATTR,i=a+1))}if(e===S_TEXT||e===S_RAW)i<a&&s.push(new Token(T_TEXT,t.slice(i,a),r));else if(e===S_COMMENT)i<a&&s.push(new Token(T_COMMENT,t.slice(i,a),r));else if(e===S_OPEN){if(i<a){let n=t.slice(i,a);s.push(new Token(T_TAG_START,n,r)),this.tag=n,e=S_ATTR}}else e===S_ATTR_KEY?(s.push(new Token(T_ATTR_KEY,t.slice(i,a),r)),e=S_ATTR):e===S_ATTR_KEY_WS?e=S_ATTR:e===S_ATTR_VALUE?(s.push(new Token(T_ATTR_VALUE,t.slice(i,a),r)),e=S_ATTR):e!==S_ATTR_VALUE_SQ&&e!==S_ATTR_VALUE_DQ||i<a&&s.push(new Token(T_ATTR_PART,t.slice(i,a),r));this.state=e}pushValue(t){let e=T_NONE;switch(this.state){case S_TEXT:case S_RAW:e=T_TEXT;break;case S_COMMENT:e=T_COMMENT;break;case S_OPEN:e=T_TAG_START,this.tag=t,this.state=S_ATTR;break;case S_ATTR:e=T_ATTR_MAP;break;case S_ATTR_VALUE_WS:e=T_ATTR_VALUE,this.state=S_ATTR;break;case S_ATTR_VALUE_SQ:case S_ATTR_VALUE_DQ:e=T_ATTR_PART}if(e!==T_NONE){let s=new Token(e,t);s.mutable=!0,this.tokens.push(s)}}end(){let t=this.tokens,e=0,s=t.length;return 0===s?t:(wsToken(t[0])&&e++,wsToken(t[s-1])&&s--,0===e&&s===t.length?t:t.slice(e,s))}}function tokenize(t){if(0===t.length)return[];let e=new Parser;e.parseChunk(t[0]);for(let s=1;s<t.length;s++)e.pushValue(""),e.parseChunk(t[s]);return e.end()}function walk(t,e,s,n,r){for(;t<s.length;++t){let i=s[t];switch(i.type){case T_TAG_START:{let a=n.read(i);if("string"==typeof a&&"/"===a[0]){for(;t<s.length&&s[++t].type!==T_TAG_END;);return t}let o=r.createElement(a,e);t=walk(t+1,o,s,n,r),r.appendChild(e,r.finishElement(o));break}case T_TAG_END:if("/"===i.value)return t;break;case T_TEXT:r.appendChild(e,n.read(i));break;case T_COMMENT:r.appendChild(e,r.createComment(n.read(i),e));break;case T_ATTR_MAP:r.setAttributes(e,n.read(i));break;case T_ATTR_KEY:{let a=n.read(i);switch(t+1<s.length?s[t+1].type:T_NONE){case T_ATTR_VALUE:r.setAttribute(e,a,n.read(s[++t]));break;case T_ATTR_PART:{let i=[n.read(s[++t])];for(;t+1<s.length&&s[t+1].type===T_ATTR_PART;)i.push(n.read(s[++t]));r.setAttributeParts(e,a,i);break}default:r.setAttribute(e,a,!0)}}}}}class Vals{constructor(t,e){this.index=0,this.values=t,this.actions=e}read(t){return t.mutable?this.actions.mapValue(this.values[this.index++]):t.value}}class TemplateResult{constructor(t,e){let s=TemplateResult.cache.get(t);s||(s=tokenize(t.raw),s.source={},TemplateResult.cache.set(t,s)),this[$tokens]=s,this.source=s.source,this.values=e}evaluate(t){let e=t.createRoot();return walk(0,e,this[$tokens],new Vals(this.values,t),t),t.finishRoot(e)}}TemplateResult.cache=new WeakMap;try{(new WeakMap).set({},1).get({})}catch(t){TemplateResult.cache=new Map}function html(t,...e){return new TemplateResult(t,e)}const HTML_NS="http://www.w3.org/1999/xhtml",SVG_NS="http://www.w3.org/2000/svg";function doc(t){return t.ownerDocument}function convertToString(t){return null==t?"":"string"==typeof t?t:String(t)}function setAttr(t,e,s){null==s||!1===s?t.removeAttribute(e):t.setAttribute(e,!0===s?e:s)}function setTextValue(t,e){t.nodeValue=convertToString(e)}function createText(t,e){return doc(e).createTextNode(convertToString(t))}function insertMarker(t,e){let s=doc(t).createTextNode("");return t.insertBefore(s,e),s}function createElement(t,e){let s=getNamespace(t,e);return s===HTML_NS?doc(e).createElement(t):doc(e).createElementNS(s,t)}function firstChild(t){return t.firstChild}function parent(t){return t?t.parentNode:null}function insertChild(t,e,s=null){e.insertBefore(t,s)}function insertSiblings(t,e,s){let n=s.parentNode;for(let r;t&&(r=t.nextSibling,n.insertBefore(t,s),t!==e);t=r);}function removeSiblings(t,e){let s=t.parentNode;for(let n;t&&(n=t.nextSibling,s.removeChild(t),t!==e);t=n);}function getNamespace(t,e){return"svg"===t?SVG_NS:e.namespaceURI||HTML_NS}function isObjectLike(t){return t&&("function"==typeof t||"object"==typeof t)}function toAttributeValue(t){if(!isObjectLike(t))return t;if(Array.isArray(t))return t.join(" ");if(t[Symbol.iterator])return Array.from(t).join(" ");throw new TypeError("Invalid attribute value")}class CommentUpdater{update(){}}class AttributeUpdater{constructor(t,e){this.node=t,this.name=e,this.last=void 0}update(t){let e=toAttributeValue(t);e!==this.last&&(this.last=e,setAttr(this.node,this.name,e))}}class AttributePartUpdater{constructor(t,e,s,n){this.node=t,this.name=e,this.parts=s,this.pos=n,s.pending[n]=!0}isReady(){let t=this.parts.pending;return!t||(t[this.pos]=!1,!!t.every((t=>!t))&&(this.parts.pending=null,!0))}update(t){this.parts[this.pos]=toAttributeValue(t),this.isReady()&&setAttr(this.node,this.name,this.parts.join(""))}}class PropertyMapUpdater{constructor(t){this.node=t}update(t){if("function"==typeof t&&(t=t(this.node)),null!=t){if("object"!=typeof t)throw new Error("Invalid property map value");Object.assign(this.node,t)}}}class ChildUpdater{constructor(t,e,s){this.slot=createSlot(t,e,s,null)}update(t){this.slot=updateSlot(this.slot,t)}}class Dynamic{constructor(t){this.value=t}}function isDynamic(t){return t instanceof Dynamic}class Actions{constructor(t,e,s){this.context=t,this.root=e,this.next=s,this.updaters=[]}createRoot(){return this.root}finishRoot(){return this.updaters}createElement(t,e){if("string"!=typeof t)throw new TypeError("Tag name must be a string");return createElement(t,e)}createComment(t){return isDynamic(t)&&this.updaters.push(new CommentUpdater),null}finishElement(t){return t}appendChild(t,e){let s=t===this.root?this.next:null;isDynamic(e)?this.updaters.push(new ChildUpdater(this.context,t,s)):null!==e&&("string"==typeof e&&(e=createText(e,t)),insertChild(e,t,s))}mapValue(t){return new Dynamic(t)}setAttribute(t,e,s){isDynamic(s)?this.updaters.push(new AttributeUpdater(t,e)):setAttr(t,e,s)}setAttributes(t){this.updaters.push(new PropertyMapUpdater(t))}setAttributeParts(t,e,s){s.pending=[];for(let n=0;n<s.length;++n)isDynamic(s[n])&&this.updaters.push(new AttributePartUpdater(t,e,s,n))}}const createSlotSymbol=Symbol("createSlot");function createSlot(t,e,s,n){if(isTextLike(n))return new TextSlot(t,e,s,n);if(n instanceof TemplateResult)return new TemplateSlot(t,e,s,n);if("function"==typeof n[createSlotSymbol])return n[createSlotSymbol](t,e,s);if(isIterable(n))return new MapSlot(t,e,s,n);throw new TypeError("Invalid child slot value")}function updateSlot(t,e){if(t.matches(e))return t.update(e),t;let s=t.start,n=createSlot(t.context,parent(s),s,e);return removeSlot(t),n}function removeSlot(t){t.cancelUpdates(),removeSiblings(t.start,t.end)}function withKey(t,e){return new KeyedValue(t,e)}function isTextLike(t){return null===t||"function"!=typeof t&&"object"!=typeof t}function isIterable(t){return Array.isArray(t)||t&&"string"!=typeof t&&t[Symbol.iterator]}class TextSlot{constructor(t,e,s,n){let r=createText(n,e);insertChild(r,e,s),this.context=t,this.start=r,this.end=r,this.last=n}cancelUpdates(){}matches(t){return isTextLike(t)}update(t){t!==this.last&&(this.last=t,setTextValue(this.start,t))}}class KeyedValue{constructor(t,e){this.key=t,this.value=e}}const positionKeyPrefix="wyxOoLpzQhihTM6QZ83HVA0";function convertValueToMap(t){if(t instanceof Map)return t;let e=new Map,s=0;for(let n of t)n instanceof KeyedValue?e.set(n.key,n.value):e.set(`${positionKeyPrefix}:${s}`,n),s++;return e}class MapSlotList{constructor(t,e){this.slot=t,this.key=e,this.next=this,this.previous=this,this.end=!0}insertBefore(t){this.remove();let e=t.previous;e.next=this,t.previous=this,this.previous=e,this.next=t,this.end=!1}remove(){this.next.previous=this.previous,this.previous.next=this.next,this.next=this,this.previous=this,this.end=!0}}class MapSlot{constructor(t,e,s,n){this.context=t,this.parent=e,this.map=new Map,this.list=new MapSlotList(createSlot(this.context,e,s),null),this.update(n)}get start(){return this.list.next.slot.start}get end(){return this.list.slot.end}cancelUpdates(){for(let t=this.list.next;!t.end;t=t.next)t.slot.cancelUpdates()}matches(t){return isIterable(t)}update(t){let e=convertValueToMap(t),s=this.list.next;for(e.forEach(((t,n)=>{for(;!s.end&&!e.has(s.key);)s=this.removeItem(s);s=this.updateItem(n,t,s)}));!s.end;)s=this.removeItem(s)}updateItem(t,e,s){let n=this.map.get(t);if(n&&n.slot.matches(e))n.slot.update(e),n===s?s=s.next:(insertSiblings(n.slot.start,n.slot.end,s.slot.start),n.insertBefore(s));else{let r=createSlot(this.context,this.parent,s.slot.start,e);n=new MapSlotList(r,t),n.insertBefore(s),this.map.set(t,n)}return s}removeItem(t){let e=t.next;return t.remove(),this.map.delete(t.key),removeSlot(t.slot),e}}class TemplateSlot{constructor(t,e,s,n){this.context=t,this.start=insertMarker(e,s),this.end=insertMarker(e,s),this.source=n.source,this.updaters=n.evaluate(new Actions(t,e,this.end)),this.pending=Array(this.updaters.length),this.update(n)}cancelUpdates(){for(let t=0;t<this.updaters.length;++t){this.cancelPending(t);let e=this.updaters[t];e.slot&&e.slot.cancelUpdates()}}matches(t){return t instanceof TemplateResult&&t.source===this.source}update(t){let{values:e}=t;for(let t=0;t<this.updaters.length;++t){let s=e[t];s&&s[Symbol.asyncIterator]?this.awaitAsyncIterator(s,t):(this.cancelPending(t),this.updaters[t].update(s))}}pendingSource(t){return this.pending[t]&&this.pending[t].source}cancelPending(t){let e=this.pending[t];e&&(this.pending[t]=null,e.cancel())}setPending(t,e){this.cancelPending(e),this.pending[e]=t}awaitAsyncIterator(t,e){if(this.pendingSource(e)===t)return;let s=t[Symbol.asyncIterator](),n=()=>{s.next().then((t=>{if(!r.cancelled)if(t.done)this.pending[e]=null;else{try{this.updaters[e].update(t.value)}catch(t){throw this.cancelPending(e),t}n()}}),(t=>{throw r.cancelled||(this.pending[e]=null),t}))},r={source:t,cancelled:!1,cancel(){this.cancelled=!0,"function"==typeof s.return&&s.return()}};n(),this.setPending(r,e)}}const slotMap=new WeakMap;function applyTemplate(t,e){if("string"==typeof t&&"object"==typeof document&&(t=document.querySelector(t)),!t||"function"!=typeof t.appendChild)throw new TypeError(`${t} is not a valid DOM target`);if(!(e instanceof TemplateResult))throw new TypeError(`${e} is not a TemplateResult object`);let s=slotMap.get(t);if(s)s=updateSlot(s,e);else{let n=firstChild(t);s=createSlot(null,t,n,e),n&&removeSiblings(n,null)}slotMap.set(t,s)}export{applyTemplate,createSlot,createSlotSymbol,html,removeSlot,updateSlot,withKey}; | ||
| function makeEnum(t=0){return new Proxy({},{get:()=>t++})}const{S_TEXT:S_TEXT,S_RAW:S_RAW,S_OPEN:S_OPEN,S_ATTR:S_ATTR,S_ATTR_CLOSE:S_ATTR_CLOSE,S_ATTR_KEY:S_ATTR_KEY,S_ATTR_KEY_WS:S_ATTR_KEY_WS,S_ATTR_VALUE_WS:S_ATTR_VALUE_WS,S_ATTR_VALUE:S_ATTR_VALUE,S_ATTR_VALUE_SQ:S_ATTR_VALUE_SQ,S_ATTR_VALUE_DQ:S_ATTR_VALUE_DQ,S_COMMENT:S_COMMENT}=makeEnum(),ESC_RE=/&(?:(lt|gt|amp|quot)|#([0-9]+)|#x([0-9a-f]+));?/gi,NAMED_REFS={lt:"<",gt:">",amp:"&",quot:'"'};function notReached(t){throw new Error(t)}function rmatch(t,e,s){return e>=s.length&&t.slice(e-s.length,e)===s}function wsChar(t){switch(t){case" ":case"\n":case"\r":case"\t":case"\f":case"\v":return!0}return t.charCodeAt(0)>128&&/\s/.test(t)}function attrChar(t){return!wsChar(t)&&'"'!==t&&"'"!==t&&"="!==t&&"/"!==t}function rawTag(t){return"script"===t||"style"===t}function escape(t){return t.replace(ESC_RE,((t,e,s,r)=>e?NAMED_REFS[e.toLowerCase()]:String.fromCharCode(parseInt(s||r,r?16:10))))}function maybeEscape(t,e){return t?escape(e):e}class ParseNode{constructor(t){this.kind=t,this.tag="",this.attributes=[],this.children=[]}}class Attribute{constructor(t,e,s){this.kind=t,this.name=e,this.value=s}}class Parser{constructor(){this.node=new ParseNode("root"),this.stack=[this.node],this.state=S_TEXT,this.attrName="",this.attrParts=[],this.tag="",this.closing=!1}addAttr(t){this.node.attributes.push(new Attribute("static",this.attrName,t))}addAttrParts(){let t=new Attribute("parts",this.attrName,this.attrParts);this.node.attributes.push(t),this.attrParts=[]}parseChunk(t){let e=this.state,s=e===S_ATTR_VALUE_DQ||e===S_ATTR_VALUE_SQ,r=!1,i=0,a=0;for(;a<t.length;++a){let n=t[a];if(e===S_RAW)">"===n&&rmatch(t,a,"</"+this.tag)&&(a-=this.tag.length+3,e=S_TEXT);else if(e===S_COMMENT)">"===n&&rmatch(t,a,"--")&&(e=S_TEXT,r=!1,i=a+1);else if("&"===n)r=!0;else if(e===S_TEXT){if("<"===n){if(a>i){let e=maybeEscape(r,t.slice(i,a));this.node.children.push(e)}e=S_OPEN,r=!1,this.closing=!1,i=a+1}}else if(e===S_ATTR_VALUE_SQ){if("'"===n){let n=maybeEscape(r,t.slice(i,a));s?(this.attrParts.push(n),this.addAttrParts(),s=!1):this.addAttr(n),e=S_ATTR,r=!1,this.attrName="",i=a+1}}else if(e===S_ATTR_VALUE_DQ){if('"'===n){let n=maybeEscape(r,t.slice(i,a));s?(this.attrParts.push(n),this.addAttrParts(),s=!1):this.addAttr(n),e=S_ATTR,r=!1,this.attrName="",i=a+1}}else">"===n?(e===S_OPEN?(this.tag=maybeEscape(r,t.slice(i,a)),this.closing||this.pushNode()):e===S_ATTR_KEY?(this.attrName=maybeEscape(r,t.slice(i,a)),this.addAttr(!0)):e===S_ATTR_KEY_WS?this.addAttr(!0):e===S_ATTR_VALUE?this.addAttr(maybeEscape(r,t.slice(i,a))):e===S_ATTR_VALUE_WS&&this.addAttr(!0),this.closing||rmatch(t,a,"/")?(this.popNode(),e=S_TEXT):e=rawTag(this.tag)?S_RAW:S_TEXT,r=!1,i=a+1):e===S_OPEN?"-"===n&&"!-"===t.slice(i,a)?(e=S_COMMENT,i=a+1):"/"===n&&a===i?this.closing=!0:attrChar(n)||(this.tag=maybeEscape(r,t.slice(i,a)),this.closing?e=S_ATTR_CLOSE:(this.pushNode(),e=S_ATTR),r=!1,i=a+1):e===S_ATTR_CLOSE||(e===S_ATTR?attrChar(n)&&(e=S_ATTR_KEY,i=a):e===S_ATTR_KEY?"="===n?(this.attrName=maybeEscape(r,t.slice(i,a)),r=!1,e=S_ATTR_VALUE_WS,i=a+1):attrChar(n)||(this.attrName=maybeEscape(r,t.slice(i,a)),r=!1,e=S_ATTR_KEY_WS,i=a+1):e===S_ATTR_KEY_WS?"="===n?(e=S_ATTR_VALUE_WS,i=a+1):attrChar(n)&&(this.addAttr(!0),e=S_ATTR_KEY,i=a):e===S_ATTR_VALUE_WS?'"'===n?(e=S_ATTR_VALUE_DQ,i=a+1):"'"===n?(e=S_ATTR_VALUE_SQ,i=a+1):attrChar(n)&&(e=S_ATTR_VALUE,i=a):e===S_ATTR_VALUE&&(attrChar(n)||(this.addAttr(maybeEscape(r,t.slice(i,a))),r=!1,e=S_ATTR,i=a+1)))}if(e===S_TEXT||e===S_RAW){if(i<a){let e=maybeEscape(r,t.slice(i,a));this.node.children.push(e)}}else if(e===S_COMMENT);else if(e===S_OPEN)this.tag=maybeEscape(r,t.slice(i,a)),this.closing?e=S_ATTR_CLOSE:(this.pushNode(),e=S_ATTR);else if(e===S_ATTR_KEY)this.attrName=maybeEscape(r,t.slice(i,a)),e=S_ATTR;else if(e===S_ATTR_KEY_WS)e=S_ATTR;else if(e===S_ATTR_VALUE)this.addAttr(maybeEscape(r,t.slice(i,a))),e=S_ATTR;else if(e===S_ATTR_VALUE_SQ||e===S_ATTR_VALUE_DQ){let e=maybeEscape(r,t.slice(i,a));this.attrParts.push(e)}this.state=e}pushNode(){let t=new ParseNode("element");t.tag=this.tag,this.node.children.push(t),this.node=t,this.stack.push(t)}popNode(){this.stack.length>1&&(this.stack.pop(),this.node=this.stack[this.stack.length-1])}addSlot(){switch(this.state){case S_TEXT:case S_RAW:this.node.children.push(new ParseNode("child-slot"));break;case S_COMMENT:this.node.children.push(new ParseNode("null-slot"));break;case S_ATTR:this.node.attributes.push(new Attribute("map","",""));break;case S_ATTR_CLOSE:this.node.attributes.push(new Attribute("null","",""));break;case S_ATTR_VALUE_WS:this.node.attributes.push(new Attribute("value",this.attrName,"")),this.state=S_ATTR;break;case S_ATTR_VALUE_SQ:case S_ATTR_VALUE_DQ:break;default:notReached("Unexpected parser state")}}end(){for(this.attrParts.length>0&&this.addAttrParts();this.stack.length>1;)this.popNode();return this.node}}function parse(t){let e=new Parser;if(t.length>0){e.parseChunk(t[0]);for(let s=1;s<t.length;s++)e.addSlot(),e.parseChunk(t[s])}return e.end()}class TemplateResult{constructor(t,e){this.template=t,this.values=e}}function html(t,...e){let s=TemplateResult.cache.get(t);return s||(s=parse(t.raw),TemplateResult.cache.set(t,s)),new TemplateResult(s,e)}TemplateResult.cache=new WeakMap;const HTML_NS="http://www.w3.org/1999/xhtml",SVG_NS="http://www.w3.org/2000/svg";function doc(t){return t.ownerDocument}function convertToString(t){return null==t?"":"string"==typeof t?t:String(t)}function setAttr(t,e,s){null==s||!1===s?t.removeAttribute(e):t.setAttribute(e,!0===s?e:s)}function setTextValue(t,e){t.nodeValue=convertToString(e)}function createText(t,e){return doc(e).createTextNode(convertToString(t))}function insertMarker(t,e){let s=doc(t).createTextNode("");return t.insertBefore(s,e),s}function createElement(t,e){let s=getNamespace(t,e);return s===HTML_NS?doc(e).createElement(t):doc(e).createElementNS(s,t)}function firstChild(t){return t.firstChild}function parent(t){return t?t.parentNode:null}function insertChild(t,e,s=null){e.insertBefore(t,s)}function insertSiblings(t,e,s){let r=s.parentNode;for(let i;t&&(i=t.nextSibling,r.insertBefore(t,s),t!==e);t=i);}function removeSiblings(t,e){let s=t.parentNode;for(let r;t&&(r=t.nextSibling,s.removeChild(t),t!==e);t=r);}function getNamespace(t,e){return"svg"===t?SVG_NS:e.namespaceURI||HTML_NS}function isObjectLike(t){return t&&("function"==typeof t||"object"==typeof t)}function toAttributeValue(t){if(!isObjectLike(t))return t;if(Array.isArray(t))return t.join(" ");if(t[Symbol.iterator])return Array.from(t).join(" ");throw new TypeError("Invalid attribute value")}class CommentUpdater{update(){}}class AttributeUpdater{constructor(t,e){this.node=t,this.name=e,this.last=void 0}update(t){let e=toAttributeValue(t);e!==this.last&&(this.last=e,setAttr(this.node,this.name,e))}}class AttributePartUpdater{constructor(t,e,s,r){this.node=t,this.name=e,this.parts=s,this.pos=r,s.pending[r]=!0}isReady(){let t=this.parts.pending;return!t||(t[this.pos]=!1,!!t.every((t=>!t))&&(this.parts.pending=null,!0))}update(t){this.parts[this.pos]=toAttributeValue(t),this.isReady()&&setAttr(this.node,this.name,this.parts.join(""))}}class PropertyMapUpdater{constructor(t){this.node=t}update(t){if("function"==typeof t&&(t=t(this.node)),null!=t){if("object"!=typeof t)throw new Error("Invalid property map value");Object.assign(this.node,t)}}}class ChildUpdater{constructor(t,e){this.slot=createSlot(t,e,null)}update(t){this.slot=updateSlot(this.slot,t)}}class Builder{constructor(){this.updaters=null}build(t,e,s){return this.updaters=[],this.buildNode(t,e,s),this.updaters}addAttributePartUpdater(t,e,s){let r=[s[0]];r.pending=[];for(let i=1;i<s.length;++i){let a=r.length;r.push(""),r.push(s[i]);let n=new AttributePartUpdater(t,e,r,a);this.updaters.push(n)}}buildNode(t,e,s){switch(t.kind){case"root":return void this.buildChildren(t,e,s);case"child-slot":return void this.updaters.push(new ChildUpdater(e,s));case"null-slot":return void this.updaters.push(new CommentUpdater)}let r=createElement(t.tag,e);for(let e of t.attributes)switch(e.kind){case"static":setAttr(r,e.name,e.value);break;case"value":this.updaters.push(new AttributeUpdater(r,e.name));break;case"map":this.updaters.push(new PropertyMapUpdater(r));break;case"null":this.updaters.push(new CommentUpdater);break;case"parts":this.addAttributePartUpdater(r,e.name,e.value)}this.buildChildren(t,r,null),insertChild(r,e,s)}buildChildren(t,e,s){for(let r of t.children)if("string"==typeof r){insertChild(createText(r,e),e,s)}else this.buildNode(r,e,s)}}function buildTemplate(t,e,s){return(new Builder).build(t,e,s)}const createSlotSymbol=Symbol("createSlot");function createSlot(t,e,s){if(isTextLike(s))return new TextSlot(t,e,s);if(s instanceof TemplateResult)return new TemplateSlot(t,e,s);if("function"==typeof s[createSlotSymbol])return s[createSlotSymbol](t,e);if(isIterable(s))return new MapSlot(t,e,s);throw new TypeError("Invalid child slot value")}function updateSlot(t,e){if(t.matches(e))return t.update(e),t;let s=t.start,r=createSlot(parent(s),s,e);return removeSlot(t),r}function removeSlot(t){t.cancelUpdates(),removeSiblings(t.start,t.end)}function withKey(t,e){return new KeyedValue(t,e)}function isTextLike(t){return null===t||"function"!=typeof t&&"object"!=typeof t}function isIterable(t){return Array.isArray(t)||t&&"string"!=typeof t&&t[Symbol.iterator]}class TextSlot{constructor(t,e,s){let r=createText(s,t);insertChild(r,t,e),this.start=r,this.end=r,this.last=s}cancelUpdates(){}matches(t){return isTextLike(t)}update(t){t!==this.last&&(this.last=t,setTextValue(this.start,t))}}class KeyedValue{constructor(t,e){this.key=t,this.value=e}}const positionKeyPrefix="wyxOoLpzQhihTM6QZ83HVA0";function convertValueToMap(t){if(t instanceof Map)return t;let e=new Map,s=0;for(let r of t)r instanceof KeyedValue?e.set(r.key,r.value):e.set(`${positionKeyPrefix}:${s}`,r),s++;return e}class MapSlotList{constructor(t,e){this.slot=t,this.key=e,this.next=this,this.previous=this,this.end=!0}insertBefore(t){this.remove();let e=t.previous;e.next=this,t.previous=this,this.previous=e,this.next=t,this.end=!1}remove(){this.next.previous=this.previous,this.previous.next=this.next,this.next=this,this.previous=this,this.end=!0}}class MapSlot{constructor(t,e,s){this.parent=t,this.map=new Map,this.list=new MapSlotList(createSlot(t,e),null),this.update(s)}get start(){return this.list.next.slot.start}get end(){return this.list.slot.end}cancelUpdates(){for(let t=this.list.next;!t.end;t=t.next)t.slot.cancelUpdates()}matches(t){return isIterable(t)}update(t){let e=convertValueToMap(t),s=this.list.next;for(e.forEach(((t,r)=>{for(;!s.end&&!e.has(s.key);)s=this.removeItem(s);s=this.updateItem(r,t,s)}));!s.end;)s=this.removeItem(s)}updateItem(t,e,s){let r=this.map.get(t);if(r&&r.slot.matches(e))r.slot.update(e),r===s?s=s.next:(insertSiblings(r.slot.start,r.slot.end,s.slot.start),r.insertBefore(s));else{let i=createSlot(this.parent,s.slot.start,e);r=new MapSlotList(i,t),r.insertBefore(s),this.map.set(t,r)}return s}removeItem(t){let e=t.next;return t.remove(),this.map.delete(t.key),removeSlot(t.slot),e}}class TemplateSlot{constructor(t,e,s){this.start=insertMarker(t,e),this.end=insertMarker(t,e),this.template=s.template,this.updaters=buildTemplate(this.template,t,this.end),this.pending=Array(this.updaters.length),this.update(s)}cancelUpdates(){for(let t=0;t<this.updaters.length;++t){this.cancelPending(t);let e=this.updaters[t];e.slot&&e.slot.cancelUpdates()}}matches(t){return t instanceof TemplateResult&&t.template===this.template}update(t){let{values:e}=t;for(let t=0;t<this.updaters.length;++t){let s=e[t];s&&s[Symbol.asyncIterator]?this.awaitAsyncIterator(s,t):(this.cancelPending(t),this.updaters[t].update(s))}}pendingSource(t){return this.pending[t]&&this.pending[t].source}cancelPending(t){let e=this.pending[t];e&&(this.pending[t]=null,e.cancel())}setPending(t,e){this.cancelPending(e),this.pending[e]=t}awaitAsyncIterator(t,e){if(this.pendingSource(e)===t)return;let s=t[Symbol.asyncIterator](),r=()=>{s.next().then((t=>{if(!i.cancelled)if(t.done)this.pending[e]=null;else{try{this.updaters[e].update(t.value)}catch(t){throw this.cancelPending(e),t}r()}}),(t=>{throw i.cancelled||(this.pending[e]=null),t}))},i={source:t,cancelled:!1,cancel(){this.cancelled=!0,"function"==typeof s.return&&s.return()}};r(),this.setPending(i,e)}}const slotMap=new WeakMap;function applyTemplate(t,e){if("string"==typeof t&&"object"==typeof document&&(t=document.querySelector(t)),!t||"function"!=typeof t.appendChild)throw new TypeError(`${t} is not a valid DOM target`);if(!(e instanceof TemplateResult))throw new TypeError(`${e} is not a TemplateResult object`);let s=slotMap.get(t);if(s)s=updateSlot(s,e);else{let r=firstChild(t);s=createSlot(t,r,e),r&&removeSiblings(r,null)}slotMap.set(t,s)}export{TemplateResult,applyTemplate,createSlot,createSlotSymbol,html,removeSlot,updateSlot,withKey}; |
+5
-5
| { | ||
| "name": "straylight", | ||
| "version": "0.8.0", | ||
| "version": "0.8.1", | ||
| "description": "HTML templating and update engine", | ||
@@ -12,13 +12,13 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "htmltag": "^0.6.5" | ||
| "htmltag": "^0.7.0" | ||
| }, | ||
| "devDependencies": { | ||
| "async-iteration-buffer": "^0.2.1", | ||
| "eslint": "^8.34.0", | ||
| "eslint": "^8.46.0", | ||
| "express": "^4.18.2", | ||
| "gzip-size-cli": "^5.1.0", | ||
| "moon-unit": "^0.3.2", | ||
| "rollup": "^3.17.2", | ||
| "rollup": "^3.28.0", | ||
| "rollup-plugin-node-resolve": "^5.2.0", | ||
| "terser": "^5.16.4" | ||
| "terser": "^5.19.2" | ||
| }, | ||
@@ -25,0 +25,0 @@ "main": "src/index.js", |
+2
-1
@@ -29,3 +29,3 @@ import { html, TemplateResult } from 'htmltag'; | ||
| let next = dom.firstChild(target); | ||
| slot = createSlot(null, target, next, template); | ||
| slot = createSlot(target, next, template); | ||
| if (next) { | ||
@@ -40,2 +40,3 @@ dom.removeSiblings(next, null); | ||
| html, | ||
| TemplateResult, | ||
| applyTemplate, | ||
@@ -42,0 +43,0 @@ createSlotSymbol, |
+19
-22
| import { TemplateResult } from 'htmltag'; | ||
| import { Actions } from './actions.js'; | ||
| import { buildTemplate } from './builder.js'; | ||
@@ -8,17 +8,17 @@ import * as dom from './dom.js'; | ||
| export function createSlot(context, parent, next, value) { | ||
| export function createSlot(parent, next, value) { | ||
| if (isTextLike(value)) { | ||
| return new TextSlot(context, parent, next, value); | ||
| return new TextSlot(parent, next, value); | ||
| } | ||
| if (value instanceof TemplateResult) { | ||
| return new TemplateSlot(context, parent, next, value); | ||
| return new TemplateSlot(parent, next, value); | ||
| } | ||
| if (typeof value[createSlotSymbol] === 'function') { | ||
| return value[createSlotSymbol](context, parent, next); | ||
| return value[createSlotSymbol](parent, next); | ||
| } | ||
| if (isIterable(value)) { | ||
| return new MapSlot(context, parent, next, value); | ||
| return new MapSlot(parent, next, value); | ||
| } | ||
@@ -35,3 +35,3 @@ | ||
| let next = slot.start; | ||
| let newSlot = createSlot(slot.context, dom.parent(next), next, value); | ||
| let newSlot = createSlot(dom.parent(next), next, value); | ||
| removeSlot(slot); | ||
@@ -65,6 +65,5 @@ return newSlot; | ||
| class TextSlot { | ||
| constructor(context, parent, next, value) { | ||
| constructor(parent, next, value) { | ||
| let node = dom.createText(value, parent); | ||
| dom.insertChild(node, parent, next); | ||
| this.context = context; | ||
| this.start = node; | ||
@@ -146,7 +145,6 @@ this.end = node; | ||
| class MapSlot { | ||
| constructor(context, parent, next, value) { | ||
| this.context = context; | ||
| constructor(parent, next, value) { | ||
| this.parent = parent; | ||
| this.map = new Map(); | ||
| this.list = new MapSlotList(createSlot(this.context, parent, next), null); | ||
| this.list = new MapSlotList(createSlot(parent, next), null); | ||
| this.update(value); | ||
@@ -198,3 +196,3 @@ } | ||
| } else { | ||
| let slot = createSlot(this.context, this.parent, next.slot.start, value); | ||
| let slot = createSlot(this.parent, next.slot.start, value); | ||
| item = new MapSlotList(slot, key); | ||
@@ -217,4 +215,3 @@ item.insertBefore(next); | ||
| class TemplateSlot { | ||
| constructor(context, parent, next, template) { | ||
| this.context = context; | ||
| constructor(parent, next, value) { | ||
| // The first and last nodes of the template could be dynamic, | ||
@@ -224,6 +221,6 @@ // so create stable marker nodes before and after the content. | ||
| this.end = dom.insertMarker(parent, next); | ||
| this.source = template.source; | ||
| this.updaters = template.evaluate(new Actions(context, parent, this.end)); | ||
| this.template = value.template; | ||
| this.updaters = buildTemplate(this.template, parent, this.end); | ||
| this.pending = Array(this.updaters.length); | ||
| this.update(template); | ||
| this.update(value); | ||
| } | ||
@@ -244,9 +241,9 @@ | ||
| value instanceof TemplateResult && | ||
| value.source === this.source | ||
| value.template === this.template | ||
| ); | ||
| } | ||
| update(template) { | ||
| // Assert: template.source === this.updater.source | ||
| let { values } = template; | ||
| update(value) { | ||
| // Assert: value.template === this.template | ||
| let { values } = value; | ||
| for (let i = 0; i < this.updaters.length; ++i) { | ||
@@ -253,0 +250,0 @@ let value = values[i]; |
+2
-2
@@ -94,4 +94,4 @@ import { createSlot, updateSlot } from './slots.js'; | ||
| export class ChildUpdater { | ||
| constructor(context, parent, next) { | ||
| this.slot = createSlot(context, parent, next, null); | ||
| constructor(parent, next) { | ||
| this.slot = createSlot(parent, next, null); | ||
| } | ||
@@ -98,0 +98,0 @@ |
+3
-1
@@ -87,3 +87,5 @@ const htmlEscapes = { | ||
| get innerHTML() { | ||
| return rawTags.test(this.parentNode.nodeName) ? this.nodeValue : esc(this.nodeValue); | ||
| return rawTags.test(this.parentNode.nodeName) | ||
| ? this.nodeValue | ||
| : esc(this.nodeValue); | ||
| } | ||
@@ -90,0 +92,0 @@ |
| import * as dom from './dom.js'; | ||
| import { | ||
| CommentUpdater, | ||
| AttributeUpdater, | ||
| AttributePartUpdater, | ||
| PropertyMapUpdater, | ||
| ChildUpdater } from './updaters.js'; | ||
| class Dynamic { | ||
| constructor(value) { this.value = value; } | ||
| } | ||
| function isDynamic(x) { | ||
| return x instanceof Dynamic; | ||
| } | ||
| export class Actions { | ||
| constructor(context, root, next) { | ||
| this.context = context; | ||
| this.root = root; | ||
| this.next = next; | ||
| this.updaters = []; | ||
| } | ||
| createRoot() { | ||
| return this.root; | ||
| } | ||
| finishRoot() { | ||
| return this.updaters; | ||
| } | ||
| createElement(tag, parent) { | ||
| if (typeof tag !== 'string') { | ||
| throw new TypeError('Tag name must be a string'); | ||
| } | ||
| return dom.createElement(tag, parent); | ||
| } | ||
| createComment(value) { | ||
| if (isDynamic(value)) { | ||
| this.updaters.push(new CommentUpdater()); | ||
| } | ||
| return null; | ||
| } | ||
| finishElement(node) { | ||
| return node; | ||
| } | ||
| appendChild(node, child) { | ||
| let next = node === this.root ? this.next : null; | ||
| if (isDynamic(child)) { | ||
| this.updaters.push(new ChildUpdater(this.context, node, next)); | ||
| } else if (child !== null) { | ||
| if (typeof child === 'string') { | ||
| child = dom.createText(child, node); | ||
| } | ||
| dom.insertChild(child, node, next); | ||
| } | ||
| } | ||
| mapValue(v) { | ||
| return new Dynamic(v); | ||
| } | ||
| setAttribute(node, name, value) { | ||
| if (isDynamic(value)) { | ||
| this.updaters.push(new AttributeUpdater(node, name)); | ||
| } else { | ||
| dom.setAttr(node, name, value); | ||
| } | ||
| } | ||
| setAttributes(node) { | ||
| this.updaters.push(new PropertyMapUpdater(node)); | ||
| } | ||
| setAttributeParts(node, name, parts) { | ||
| parts.pending = []; | ||
| for (let i = 0; i < parts.length; ++i) { | ||
| if (isDynamic(parts[i])) { | ||
| this.updaters.push(new AttributePartUpdater(node, name, parts, i)); | ||
| } | ||
| } | ||
| } | ||
| } |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
65385
-2.45%1604
-2.91%7
16.67%+ Added
- Removed
Updated