-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathparser.js
More file actions
93 lines (85 loc) · 2.88 KB
/
Copy pathparser.js
File metadata and controls
93 lines (85 loc) · 2.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import { ATTRIBUTE_NODE, TEXT_NODE, COMMENT_NODE } from 'domconstants/constants';
import { TEXT_ELEMENTS } from 'domconstants/re';
import parser from '@webreflection/uparser';
import { empty, isArray, set } from './utils.js';
import { cel, entry } from './literals.js';
import { attribute, text, arrayComment, boundComment, removeAttribute } from './handler.js';
import createContent from './create-content.js';
/** @typedef {import("./literals.js").Entry} Entry */
/**
* @typedef {Object} Resolved
* @property {DocumentFragment} content
* @property {Entry[]} entries
* @property {function[]} updates
* @property {number} length
*/
/**
* @param {Element} node
* @returns {number[]}
*/
const createPath = node => {
const path = [];
let parentNode;
while ((parentNode = node.parentNode)) {
path.push(path.indexOf.call(parentNode.childNodes, node));
node = parentNode;
}
return path;
};
/**
* @param {TemplateStringsArray} template
* @param {boolean} xml
* @returns {Resolved}
*/
const resolve = (template, values, xml) => {
const content = createContent(parser(template, prefix, xml), xml);
const { length } = template;
let asArray = false, entries = empty;
if (length > 1) {
const tw = document.createTreeWalker(content, 1 | 128);
const replace = [];
let i = 0, search = `${prefix}${i++}`;
entries = [];
while (i < length) {
const node = tw.nextNode();
if (node.nodeType === COMMENT_NODE) {
if (node.data === search) {
let update = isArray(values[i - 1]) ? arrayComment : boundComment;
if (update === boundComment) replace.push(node);
else asArray = true;
entries.push(entry(COMMENT_NODE, createPath(node), update));
search = `${prefix}${i++}`;
}
}
else {
let path;
while (node.hasAttribute(search)) {
if (!path) path = createPath(node);
const name = node.getAttribute(search);
entries.push(entry(ATTRIBUTE_NODE, path, attribute(node, name, xml), name));
removeAttribute(node, search);
search = `${prefix}${i++}`;
}
if (
TEXT_ELEMENTS.test(node.localName) &&
node.textContent.trim() === `<!--${search}-->`
) {
entries.push(entry(TEXT_NODE, path || createPath(node), text));
search = `${prefix}${i++}`;
}
}
}
for (i = 0; i < replace.length; i++)
replace[i].replaceWith(document.createTextNode(''));
}
const l = content.childNodes.length;
return set(cache, template, cel(content, entries, l === 1 && asArray ? 0 : l));
};
/** @type {WeakMap<TemplateStringsArray, Resolved>} */
const cache = new WeakMap;
const prefix = 'isµ';
/**
* @param {boolean} xml
* @returns {(template: TemplateStringsArray, values: any[]) => Resolved}
*/
export default xml => (template, values) => cache.get(template) || resolve(template, values, xml);