@microsoft/fast-element 1.10.0 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +1 -12
- package/CHANGELOG.json +396 -1
- package/CHANGELOG.md +68 -2
- package/README.md +2 -2
- package/dist/dts/components/attributes.d.ts +4 -1
- package/dist/dts/components/controller.d.ts +12 -11
- package/dist/dts/components/fast-definitions.d.ts +8 -2
- package/dist/dts/components/fast-element.d.ts +5 -4
- package/dist/dts/debug.d.ts +1 -0
- package/dist/dts/hooks.d.ts +20 -0
- package/dist/dts/index.d.ts +16 -15
- package/dist/dts/index.debug.d.ts +2 -0
- package/dist/dts/index.rollup.d.ts +2 -0
- package/dist/dts/index.rollup.debug.d.ts +3 -0
- package/dist/dts/interfaces.d.ts +144 -0
- package/dist/dts/observation/arrays.d.ts +207 -0
- package/dist/dts/observation/behavior.d.ts +5 -5
- package/dist/dts/observation/notifier.d.ts +18 -18
- package/dist/dts/observation/observable.d.ts +86 -29
- package/dist/dts/observation/splice-strategies.d.ts +13 -0
- package/dist/dts/observation/update-queue.d.ts +40 -0
- package/dist/dts/platform.d.ts +18 -67
- package/dist/dts/polyfills.d.ts +8 -0
- package/dist/dts/styles/css-directive.d.ts +43 -5
- package/dist/dts/styles/css.d.ts +19 -3
- package/dist/dts/styles/element-styles.d.ts +42 -62
- package/dist/dts/templating/binding.d.ts +320 -64
- package/dist/dts/templating/children.d.ts +18 -15
- package/dist/dts/templating/compiler.d.ts +47 -28
- package/dist/dts/templating/dom.d.ts +41 -0
- package/dist/dts/templating/html-directive.d.ts +179 -43
- package/dist/dts/templating/markup.d.ts +48 -0
- package/dist/dts/templating/node-observation.d.ts +45 -29
- package/dist/dts/templating/ref.d.ts +6 -12
- package/dist/dts/templating/repeat.d.ts +72 -14
- package/dist/dts/templating/slotted.d.ts +13 -14
- package/dist/dts/templating/template.d.ts +78 -23
- package/dist/dts/templating/view.d.ts +16 -23
- package/dist/dts/utilities.d.ts +40 -0
- package/dist/esm/components/attributes.js +25 -24
- package/dist/esm/components/controller.js +77 -57
- package/dist/esm/components/fast-definitions.js +14 -22
- package/dist/esm/debug.js +29 -0
- package/dist/esm/hooks.js +32 -0
- package/dist/esm/index.debug.js +2 -0
- package/dist/esm/index.js +19 -14
- package/dist/esm/index.rollup.debug.js +3 -0
- package/dist/esm/index.rollup.js +2 -0
- package/dist/esm/interfaces.js +8 -1
- package/dist/esm/observation/arrays.js +269 -0
- package/dist/esm/observation/notifier.js +75 -83
- package/dist/esm/observation/observable.js +80 -107
- package/dist/esm/observation/{array-change-records.js → splice-strategies.js} +136 -62
- package/dist/esm/observation/update-queue.js +67 -0
- package/dist/esm/platform.js +36 -42
- package/dist/esm/polyfills.js +85 -0
- package/dist/esm/styles/css-directive.js +29 -13
- package/dist/esm/styles/css.js +27 -40
- package/dist/esm/styles/element-styles.js +65 -104
- package/dist/esm/templating/binding.js +465 -155
- package/dist/esm/templating/children.js +33 -23
- package/dist/esm/templating/compiler.js +235 -152
- package/dist/esm/templating/dom.js +49 -0
- package/dist/esm/templating/html-directive.js +125 -40
- package/dist/esm/templating/markup.js +75 -0
- package/dist/esm/templating/node-observation.js +50 -45
- package/dist/esm/templating/ref.js +7 -16
- package/dist/esm/templating/repeat.js +38 -43
- package/dist/esm/templating/slotted.js +23 -20
- package/dist/esm/templating/template.js +71 -95
- package/dist/esm/templating/view.js +44 -43
- package/dist/esm/templating/when.js +2 -1
- package/dist/esm/utilities.js +139 -0
- package/dist/fast-element.api.json +14062 -5235
- package/dist/fast-element.d.ts +1434 -579
- package/dist/fast-element.debug.js +3824 -0
- package/dist/fast-element.debug.min.js +1 -0
- package/dist/fast-element.js +3565 -4014
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +2908 -0
- package/dist/tsdoc-metadata.json +1 -1
- package/docs/api-report.md +590 -231
- package/docs/fast-element-2-changes.md +15 -0
- package/docs/guide/declaring-templates.md +5 -4
- package/docs/guide/defining-elements.md +3 -2
- package/docs/guide/leveraging-css.md +1 -0
- package/docs/guide/next-steps.md +3 -2
- package/docs/guide/observables-and-state.md +2 -1
- package/docs/guide/using-directives.md +2 -1
- package/docs/guide/working-with-shadow-dom.md +1 -0
- package/karma.conf.cjs +6 -17
- package/package.json +48 -14
- package/dist/dts/dom.d.ts +0 -112
- package/dist/dts/observation/array-change-records.d.ts +0 -48
- package/dist/dts/observation/array-observer.d.ts +0 -9
- package/dist/esm/dom.js +0 -207
- package/dist/esm/observation/array-observer.js +0 -173
|
@@ -1,45 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { HTMLDirective } from "./html-directive.js";
|
|
3
|
+
import { NodeObservationDirective } from "./node-observation.js";
|
|
3
4
|
/**
|
|
4
5
|
* The runtime behavior for child node observation.
|
|
5
6
|
* @public
|
|
6
7
|
*/
|
|
7
|
-
export class
|
|
8
|
+
export class ChildrenDirective extends NodeObservationDirective {
|
|
8
9
|
/**
|
|
9
|
-
* Creates an instance of
|
|
10
|
-
* @param
|
|
11
|
-
* @param options - The options to use when observing the element children.
|
|
10
|
+
* Creates an instance of ChildrenDirective.
|
|
11
|
+
* @param options - The options to use in configuring the child observation behavior.
|
|
12
12
|
*/
|
|
13
|
-
constructor(
|
|
14
|
-
super(
|
|
15
|
-
this.
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super(options);
|
|
15
|
+
this.observerProperty = `${this.id}-o`;
|
|
16
|
+
this.handleEvent = (mutations, observer) => {
|
|
17
|
+
const target = observer.target;
|
|
18
|
+
this.updateTarget(this.getSource(target), this.computeNodes(target));
|
|
19
|
+
};
|
|
16
20
|
options.childList = true;
|
|
17
21
|
}
|
|
18
22
|
/**
|
|
19
23
|
* Begins observation of the nodes.
|
|
24
|
+
* @param target - The target to observe.
|
|
20
25
|
*/
|
|
21
|
-
observe() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
observe(target) {
|
|
27
|
+
var _a;
|
|
28
|
+
const observer = (_a = target[this.observerProperty]) !== null && _a !== void 0 ? _a : (target[this.observerProperty] = new MutationObserver(this.handleEvent));
|
|
29
|
+
observer.target = target;
|
|
30
|
+
observer.observe(target, this.options);
|
|
26
31
|
}
|
|
27
32
|
/**
|
|
28
33
|
* Disconnects observation of the nodes.
|
|
34
|
+
* @param target - The target to unobserve.
|
|
29
35
|
*/
|
|
30
|
-
disconnect() {
|
|
31
|
-
this.
|
|
36
|
+
disconnect(target) {
|
|
37
|
+
const observer = target[this.observerProperty];
|
|
38
|
+
observer.target = null;
|
|
39
|
+
observer.disconnect();
|
|
32
40
|
}
|
|
33
41
|
/**
|
|
34
|
-
* Retrieves the nodes that should be assigned to the
|
|
42
|
+
* Retrieves the raw nodes that should be assigned to the source property.
|
|
43
|
+
* @param target - The target to get the node to.
|
|
35
44
|
*/
|
|
36
|
-
getNodes() {
|
|
37
|
-
if ("
|
|
38
|
-
return Array.from(
|
|
45
|
+
getNodes(target) {
|
|
46
|
+
if ("selector" in this.options) {
|
|
47
|
+
return Array.from(target.querySelectorAll(this.options.selector));
|
|
39
48
|
}
|
|
40
|
-
return Array.from(
|
|
49
|
+
return Array.from(target.childNodes);
|
|
41
50
|
}
|
|
42
51
|
}
|
|
52
|
+
HTMLDirective.define(ChildrenDirective);
|
|
43
53
|
/**
|
|
44
54
|
* A directive that observes the `childNodes` of an element and updates a property
|
|
45
55
|
* whenever they change.
|
|
@@ -47,10 +57,10 @@ export class ChildrenBehavior extends NodeObservationBehavior {
|
|
|
47
57
|
* @public
|
|
48
58
|
*/
|
|
49
59
|
export function children(propertyOrOptions) {
|
|
50
|
-
if (
|
|
60
|
+
if (isString(propertyOrOptions)) {
|
|
51
61
|
propertyOrOptions = {
|
|
52
62
|
property: propertyOrOptions,
|
|
53
63
|
};
|
|
54
64
|
}
|
|
55
|
-
return new
|
|
65
|
+
return new ChildrenDirective(propertyOrOptions);
|
|
56
66
|
}
|
|
@@ -1,186 +1,269 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { FAST } from "../platform.js";
|
|
3
|
+
import { Parser } from "./markup.js";
|
|
4
|
+
import { bind, oneTime } from "./binding.js";
|
|
5
|
+
import { Aspect } from "./html-directive.js";
|
|
6
|
+
import { HTMLView } from "./view.js";
|
|
7
|
+
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
8
|
+
const descriptorCache = {};
|
|
9
|
+
// used to prevent creating lots of objects just to track node and index while compiling
|
|
10
|
+
const next = {
|
|
11
|
+
index: 0,
|
|
12
|
+
node: null,
|
|
13
|
+
};
|
|
4
14
|
class CompilationContext {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.
|
|
15
|
+
constructor(fragment, directives) {
|
|
16
|
+
this.fragment = fragment;
|
|
17
|
+
this.directives = directives;
|
|
18
|
+
this.proto = null;
|
|
19
|
+
this.nodeIds = new Set();
|
|
20
|
+
this.descriptors = {};
|
|
21
|
+
this.factories = [];
|
|
8
22
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
}
|
|
17
|
-
release() {
|
|
18
|
-
/* eslint-disable-next-line @typescript-eslint/no-this-alias */
|
|
19
|
-
sharedContext = this;
|
|
20
|
-
}
|
|
21
|
-
static borrow(directives) {
|
|
22
|
-
const shareable = sharedContext || new CompilationContext();
|
|
23
|
-
shareable.directives = directives;
|
|
24
|
-
shareable.reset();
|
|
25
|
-
sharedContext = null;
|
|
26
|
-
return shareable;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function createAggregateBinding(parts) {
|
|
30
|
-
if (parts.length === 1) {
|
|
31
|
-
return parts[0];
|
|
23
|
+
addFactory(factory, parentId, nodeId, targetIndex) {
|
|
24
|
+
if (!this.nodeIds.has(nodeId)) {
|
|
25
|
+
this.nodeIds.add(nodeId);
|
|
26
|
+
this.addTargetDescriptor(parentId, nodeId, targetIndex);
|
|
27
|
+
}
|
|
28
|
+
factory.nodeId = nodeId;
|
|
29
|
+
this.factories.push(factory);
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (typeof x === "string") {
|
|
37
|
-
return () => x;
|
|
38
|
-
}
|
|
39
|
-
targetName = x.targetName || targetName;
|
|
40
|
-
return x.binding;
|
|
41
|
-
});
|
|
42
|
-
const binding = (scope, context) => {
|
|
43
|
-
let output = "";
|
|
44
|
-
for (let i = 0; i < partCount; ++i) {
|
|
45
|
-
output += finalParts[i](scope, context);
|
|
46
|
-
}
|
|
47
|
-
return output;
|
|
48
|
-
};
|
|
49
|
-
const directive = new HTMLBindingDirective(binding);
|
|
50
|
-
directive.targetName = targetName;
|
|
51
|
-
return directive;
|
|
52
|
-
}
|
|
53
|
-
const interpolationEndLength = _interpolationEnd.length;
|
|
54
|
-
function parseContent(context, value) {
|
|
55
|
-
const valueParts = value.split(_interpolationStart);
|
|
56
|
-
if (valueParts.length === 1) {
|
|
57
|
-
return null;
|
|
31
|
+
freeze() {
|
|
32
|
+
this.proto = Object.create(null, this.descriptors);
|
|
33
|
+
return this;
|
|
58
34
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
literal = current;
|
|
35
|
+
addTargetDescriptor(parentId, targetId, targetIndex) {
|
|
36
|
+
const descriptors = this.descriptors;
|
|
37
|
+
if (targetId === "r" || // root
|
|
38
|
+
targetId === "h" || // host
|
|
39
|
+
descriptors[targetId]) {
|
|
40
|
+
return;
|
|
66
41
|
}
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
42
|
+
if (!descriptors[parentId]) {
|
|
43
|
+
const index = parentId.lastIndexOf(".");
|
|
44
|
+
const grandparentId = parentId.substring(0, index);
|
|
45
|
+
const childIndex = parseInt(parentId.substring(index + 1));
|
|
46
|
+
this.addTargetDescriptor(grandparentId, parentId, childIndex);
|
|
71
47
|
}
|
|
72
|
-
|
|
73
|
-
|
|
48
|
+
let descriptor = descriptorCache[targetId];
|
|
49
|
+
if (!descriptor) {
|
|
50
|
+
const field = `_${targetId}`;
|
|
51
|
+
descriptorCache[targetId] = descriptor = {
|
|
52
|
+
get() {
|
|
53
|
+
var _a;
|
|
54
|
+
return ((_a = this[field]) !== null && _a !== void 0 ? _a : (this[field] = this[parentId].childNodes[targetIndex]));
|
|
55
|
+
},
|
|
56
|
+
};
|
|
74
57
|
}
|
|
58
|
+
descriptors[targetId] = descriptor;
|
|
59
|
+
}
|
|
60
|
+
createView(hostBindingTarget) {
|
|
61
|
+
const fragment = this.fragment.cloneNode(true);
|
|
62
|
+
const targets = Object.create(this.proto);
|
|
63
|
+
targets.r = fragment;
|
|
64
|
+
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
|
|
65
|
+
for (const id of this.nodeIds) {
|
|
66
|
+
targets[id]; // trigger locator
|
|
67
|
+
}
|
|
68
|
+
return new HTMLView(fragment, this.factories, targets);
|
|
75
69
|
}
|
|
76
|
-
return bindingParts;
|
|
77
70
|
}
|
|
78
|
-
function compileAttributes(context, node, includeBasicValues = false) {
|
|
71
|
+
function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBasicValues = false) {
|
|
79
72
|
const attributes = node.attributes;
|
|
73
|
+
const directives = context.directives;
|
|
80
74
|
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
81
75
|
const attr = attributes[i];
|
|
82
76
|
const attrValue = attr.value;
|
|
83
|
-
const parseResult =
|
|
77
|
+
const parseResult = Parser.parse(attrValue, directives);
|
|
84
78
|
let result = null;
|
|
85
79
|
if (parseResult === null) {
|
|
86
80
|
if (includeBasicValues) {
|
|
87
|
-
result =
|
|
88
|
-
result
|
|
81
|
+
result = bind(() => attrValue, oneTime);
|
|
82
|
+
Aspect.assign(result, attr.name);
|
|
89
83
|
}
|
|
90
84
|
}
|
|
91
85
|
else {
|
|
92
|
-
|
|
86
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
87
|
+
result = Compiler.aggregate(parseResult);
|
|
93
88
|
}
|
|
94
89
|
if (result !== null) {
|
|
95
90
|
node.removeAttributeNode(attr);
|
|
96
91
|
i--;
|
|
97
92
|
ii--;
|
|
98
|
-
context.addFactory(result);
|
|
93
|
+
context.addFactory(result, parentId, nodeId, nodeIndex);
|
|
99
94
|
}
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
|
-
function compileContent(context, node,
|
|
103
|
-
const parseResult =
|
|
104
|
-
if (parseResult
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
97
|
+
function compileContent(context, node, parentId, nodeId, nodeIndex) {
|
|
98
|
+
const parseResult = Parser.parse(node.textContent, context.directives);
|
|
99
|
+
if (parseResult === null) {
|
|
100
|
+
next.node = node.nextSibling;
|
|
101
|
+
next.index = nodeIndex + 1;
|
|
102
|
+
return next;
|
|
103
|
+
}
|
|
104
|
+
let currentNode;
|
|
105
|
+
let lastNode = (currentNode = node);
|
|
106
|
+
for (let i = 0, ii = parseResult.length; i < ii; ++i) {
|
|
107
|
+
const currentPart = parseResult[i];
|
|
108
|
+
if (i !== 0) {
|
|
109
|
+
nodeIndex++;
|
|
110
|
+
nodeId = targetIdFrom(parentId, nodeIndex);
|
|
111
|
+
currentNode = lastNode.parentNode.insertBefore(document.createTextNode(""), lastNode.nextSibling);
|
|
112
|
+
}
|
|
113
|
+
if (isString(currentPart)) {
|
|
114
|
+
currentNode.textContent = currentPart;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
currentNode.textContent = " ";
|
|
118
|
+
context.addFactory(currentPart, parentId, nodeId, nodeIndex);
|
|
123
119
|
}
|
|
124
|
-
|
|
120
|
+
lastNode = currentNode;
|
|
125
121
|
}
|
|
122
|
+
next.index = nodeIndex + 1;
|
|
123
|
+
next.node = lastNode.nextSibling;
|
|
124
|
+
return next;
|
|
126
125
|
}
|
|
126
|
+
function compileChildren(context, parent, parentId) {
|
|
127
|
+
let nodeIndex = 0;
|
|
128
|
+
let childNode = parent.firstChild;
|
|
129
|
+
while (childNode) {
|
|
130
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
131
|
+
const result = compileNode(context, parentId, childNode, nodeIndex);
|
|
132
|
+
childNode = result.node;
|
|
133
|
+
nodeIndex = result.index;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function compileNode(context, parentId, node, nodeIndex) {
|
|
137
|
+
const nodeId = targetIdFrom(parentId, nodeIndex);
|
|
138
|
+
switch (node.nodeType) {
|
|
139
|
+
case 1: // element node
|
|
140
|
+
compileAttributes(context, parentId, node, nodeId, nodeIndex);
|
|
141
|
+
compileChildren(context, node, nodeId);
|
|
142
|
+
break;
|
|
143
|
+
case 3: // text node
|
|
144
|
+
return compileContent(context, node, parentId, nodeId, nodeIndex);
|
|
145
|
+
case 8: // comment
|
|
146
|
+
const parts = Parser.parse(node.data, context.directives);
|
|
147
|
+
if (parts !== null) {
|
|
148
|
+
context.addFactory(
|
|
149
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
150
|
+
Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
next.index = nodeIndex + 1;
|
|
155
|
+
next.node = node.nextSibling;
|
|
156
|
+
return next;
|
|
157
|
+
}
|
|
158
|
+
function isMarker(node, directives) {
|
|
159
|
+
return (node &&
|
|
160
|
+
node.nodeType == 8 &&
|
|
161
|
+
Parser.parse(node.data, directives) !== null);
|
|
162
|
+
}
|
|
163
|
+
const templateTag = "TEMPLATE";
|
|
164
|
+
const policyOptions = { createHTML: html => html };
|
|
165
|
+
let htmlPolicy = globalThis.trustedTypes
|
|
166
|
+
? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
|
|
167
|
+
: policyOptions;
|
|
168
|
+
const fastHTMLPolicy = htmlPolicy;
|
|
127
169
|
/**
|
|
128
|
-
*
|
|
129
|
-
* result which include a cloneable DocumentFragment and factories capable
|
|
130
|
-
* of attaching runtime behavior to nodes within the fragment.
|
|
131
|
-
* @param template - The template to compile.
|
|
132
|
-
* @param directives - The directives referenced by the template.
|
|
133
|
-
* @remarks
|
|
134
|
-
* The template that is provided for compilation is altered in-place
|
|
135
|
-
* and cannot be compiled again. If the original template must be preserved,
|
|
136
|
-
* it is recommended that you clone the original and pass the clone to this API.
|
|
170
|
+
* Common APIs related to compilation.
|
|
137
171
|
* @public
|
|
138
172
|
*/
|
|
139
|
-
export
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
context.targetIndex++;
|
|
151
|
-
switch (node.nodeType) {
|
|
152
|
-
case 1: // element node
|
|
153
|
-
compileAttributes(context, node);
|
|
154
|
-
break;
|
|
155
|
-
case 3: // text node
|
|
156
|
-
compileContent(context, node, walker);
|
|
157
|
-
break;
|
|
158
|
-
case 8: // comment
|
|
159
|
-
if (DOM.isMarker(node)) {
|
|
160
|
-
context.addFactory(directives[DOM.extractDirectiveIndexFromMarker(node)]);
|
|
161
|
-
}
|
|
173
|
+
export const Compiler = {
|
|
174
|
+
/**
|
|
175
|
+
* Sets the HTML trusted types policy used by the compiler.
|
|
176
|
+
* @param policy - The policy to set for HTML.
|
|
177
|
+
* @remarks
|
|
178
|
+
* This API can only be called once, for security reasons. It should be
|
|
179
|
+
* called by the application developer at the start of their program.
|
|
180
|
+
*/
|
|
181
|
+
setHTMLPolicy(policy) {
|
|
182
|
+
if (htmlPolicy !== fastHTMLPolicy) {
|
|
183
|
+
throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
|
|
162
184
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
htmlPolicy = policy;
|
|
186
|
+
},
|
|
187
|
+
/**
|
|
188
|
+
* Compiles a template and associated directives into a compilation
|
|
189
|
+
* result which can be used to create views.
|
|
190
|
+
* @param html - The html string or template element to compile.
|
|
191
|
+
* @param directives - The directives referenced by the template.
|
|
192
|
+
* @remarks
|
|
193
|
+
* The template that is provided for compilation is altered in-place
|
|
194
|
+
* and cannot be compiled again. If the original template must be preserved,
|
|
195
|
+
* it is recommended that you clone the original and pass the clone to this API.
|
|
196
|
+
* @public
|
|
197
|
+
*/
|
|
198
|
+
compile(html, directives) {
|
|
199
|
+
let template;
|
|
200
|
+
if (isString(html)) {
|
|
201
|
+
template = document.createElement(templateTag);
|
|
202
|
+
template.innerHTML = htmlPolicy.createHTML(html);
|
|
203
|
+
const fec = template.content.firstElementChild;
|
|
204
|
+
if (fec !== null && fec.tagName === templateTag) {
|
|
205
|
+
template = fec;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
template = html;
|
|
210
|
+
}
|
|
211
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
|
|
212
|
+
const fragment = document.adoptNode(template.content);
|
|
213
|
+
const context = new CompilationContext(fragment, directives);
|
|
214
|
+
compileAttributes(context, "", template, /* host */ "h", 0, true);
|
|
215
|
+
if (
|
|
216
|
+
// If the first node in a fragment is a marker, that means it's an unstable first node,
|
|
217
|
+
// because something like a when, repeat, etc. could add nodes before the marker.
|
|
218
|
+
// To mitigate this, we insert a stable first node. However, if we insert a node,
|
|
219
|
+
// that will alter the result of the TreeWalker. So, we also need to offset the target index.
|
|
220
|
+
isMarker(fragment.firstChild, directives) ||
|
|
221
|
+
// Or if there is only one node and a directive, it means the template's content
|
|
222
|
+
// is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
|
|
223
|
+
// the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
|
|
224
|
+
(fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
|
|
225
|
+
fragment.insertBefore(document.createComment(""), fragment.firstChild);
|
|
226
|
+
}
|
|
227
|
+
compileChildren(context, fragment, /* root */ "r");
|
|
228
|
+
next.node = null; // prevent leaks
|
|
229
|
+
return context.freeze();
|
|
230
|
+
},
|
|
231
|
+
/**
|
|
232
|
+
* Sets the default compilation strategy that will be used by the ViewTemplate whenever
|
|
233
|
+
* it needs to compile a view preprocessed with the html template function.
|
|
234
|
+
* @param strategy - The compilation strategy to use when compiling templates.
|
|
235
|
+
*/
|
|
236
|
+
setDefaultStrategy(strategy) {
|
|
237
|
+
this.compile = strategy;
|
|
238
|
+
},
|
|
239
|
+
/**
|
|
240
|
+
* Aggregates an array of strings and directives into a single directive.
|
|
241
|
+
* @param parts - A heterogeneous array of static strings interspersed with
|
|
242
|
+
* directives.
|
|
243
|
+
* @returns A single inline directive that aggregates the behavior of all the parts.
|
|
244
|
+
*/
|
|
245
|
+
aggregate(parts) {
|
|
246
|
+
if (parts.length === 1) {
|
|
247
|
+
return parts[0];
|
|
248
|
+
}
|
|
249
|
+
let sourceAspect;
|
|
250
|
+
const partCount = parts.length;
|
|
251
|
+
const finalParts = parts.map((x) => {
|
|
252
|
+
if (isString(x)) {
|
|
253
|
+
return () => x;
|
|
254
|
+
}
|
|
255
|
+
sourceAspect = x.sourceAspect || sourceAspect;
|
|
256
|
+
return x.binding;
|
|
257
|
+
});
|
|
258
|
+
const binding = (scope, context) => {
|
|
259
|
+
let output = "";
|
|
260
|
+
for (let i = 0; i < partCount; ++i) {
|
|
261
|
+
output += finalParts[i](scope, context);
|
|
262
|
+
}
|
|
263
|
+
return output;
|
|
264
|
+
};
|
|
265
|
+
const directive = bind(binding);
|
|
266
|
+
Aspect.assign(directive, sourceAspect);
|
|
267
|
+
return directive;
|
|
268
|
+
},
|
|
269
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Updates } from "../observation/update-queue.js";
|
|
2
|
+
/**
|
|
3
|
+
* Common DOM APIs.
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export const DOM = Object.freeze({
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated
|
|
9
|
+
* Use Updates.enqueue().
|
|
10
|
+
*/
|
|
11
|
+
queueUpdate: Updates.enqueue,
|
|
12
|
+
/**
|
|
13
|
+
* @deprecated
|
|
14
|
+
* Use Updates.next()
|
|
15
|
+
*/
|
|
16
|
+
nextUpdate: Updates.next,
|
|
17
|
+
/**
|
|
18
|
+
* @deprecated
|
|
19
|
+
* Use Updates.process()
|
|
20
|
+
*/
|
|
21
|
+
processUpdates: Updates.process,
|
|
22
|
+
/**
|
|
23
|
+
* Sets an attribute value on an element.
|
|
24
|
+
* @param element - The element to set the attribute value on.
|
|
25
|
+
* @param attributeName - The attribute name to set.
|
|
26
|
+
* @param value - The value of the attribute to set.
|
|
27
|
+
* @remarks
|
|
28
|
+
* If the value is `null` or `undefined`, the attribute is removed, otherwise
|
|
29
|
+
* it is set to the provided value using the standard `setAttribute` API.
|
|
30
|
+
*/
|
|
31
|
+
setAttribute(element, attributeName, value) {
|
|
32
|
+
value === null || value === undefined
|
|
33
|
+
? element.removeAttribute(attributeName)
|
|
34
|
+
: element.setAttribute(attributeName, value);
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Sets a boolean attribute value.
|
|
38
|
+
* @param element - The element to set the boolean attribute value on.
|
|
39
|
+
* @param attributeName - The attribute name to set.
|
|
40
|
+
* @param value - The value of the attribute to set.
|
|
41
|
+
* @remarks
|
|
42
|
+
* If the value is true, the attribute is added; otherwise it is removed.
|
|
43
|
+
*/
|
|
44
|
+
setBooleanAttribute(element, attributeName, value) {
|
|
45
|
+
value
|
|
46
|
+
? element.setAttribute(attributeName, "")
|
|
47
|
+
: element.removeAttribute(attributeName);
|
|
48
|
+
},
|
|
49
|
+
});
|